2017-10-28 noip模拟赛by WISCO 信息组
Posted 秋千旁的蜂蝶~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2017-10-28 noip模拟赛by WISCO 信息组相关的知识,希望对你有一定的参考价值。
第一次做模拟赛,自我感觉良好(大概是这套题比较简单)
T1 名称为“数据结构”,这也太坑了点……233
要维护一个数列(初始为零),支持区间加与查询。
查询的是一个区间中有多少数满足min<=(a[i]*i%mod)<=max,其中min、max、mod是一开始给出来的,a[i]表示这个数的值,i表示这个数的编号
n<=80000,一开始修改与查询个数<=1e6,后面会跟不超过1e7个查询
我看到这道题,想到了线段树,主要觉得这样好查询一个区间内有多少满足条件的
可是,由于条件比较诡异,所以菜鸡的我只能单点修改……最后查询呢,每一次logn
这样得了50分
正解:差分!
首先看到题目后面会接一堆查询吗,那可以用前缀和维护啊!(用线段树每次都logn,前缀和只需提前处理一下然后O(1)就好了,我是何苦啊……)
前面的,就是差分了。每次区间加时a[l]+=x,a[r+1]-=x就可以了,查询时每个点这时的值就是a[1]+...+a[i]
这有些类似树状数组中的区间加诶,但前面的查询是一个点一个点查,所以不用树状数组,直接数组就行了
代码:
(特别注意:有的地方要转long long啊,幸亏样例良心,否则我要go die 了)
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 5 typedef long long ll; 6 const int MAXN = 80005; 7 ll a[MAXN]; 8 int b[MAXN]; 9 int n,opt,mod,Min,Max,Q; 10 11 int main() 12 { 13 int i,y,l,r,ans; 14 ll sum,x; 15 char ch; 16 scanf("%d%d",&n,&opt); 17 scanf("%d%d%d",&mod,&Min,&Max); 18 19 while(opt--){ 20 cin>>ch; 21 if(ch==‘A‘){ 22 scanf("%d%d%lld",&l,&r,&x); 23 a[l]+=x;a[r+1]-=x; 24 } 25 else{ 26 scanf("%d%d",&l,&r); 27 sum=0;ans=0; 28 for(i=1;i<=r;i++){ 29 sum+=a[i]; 30 if(i>=l) 31 { 32 y=(sum*i)%mod; 33 if(y>=Min && y<=Max) ans++; 34 } 35 } 36 printf("%d\n",ans); 37 } 38 } 39 40 sum=0; 41 for(i=1;i<=n;i++){ 42 sum+=a[i]; 43 y=sum*i%mod; 44 if(y>=Min && y<=Max) b[i]=b[i-1]+1; 45 else b[i]=b[i-1]; 46 } 47 scanf("%d",&Q); 48 while(Q--){ 49 scanf("%d%d",&l,&r); 50 printf("%d\n",b[r]-b[l-1]); 51 } 52 53 return 0; 54 }
T2 “答案错误”
非常有趣的一道数学题~
凭借多年积累的找规律技巧,构造出了答案 (YEAH)
具体题目及证明 略……
但是提交上去竟只有90分!!有一个点被卡常了(悲伤)
回来看看,发现是找规律没太彻底,多了两个if语句,下次要注意了……
代码:
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 5 typedef long long ll; 6 int n,Q; 7 ll k; 8 9 int main() 10 { 11 int ans,r; 12 ll x; 13 scanf("%d%d",&n,&Q); 14 15 while(Q--){ 16 scanf("%lld",&k); 17 x=k-1; 18 ans=0; 19 while(x){ 20 if(x%2==1) ans++; 21 x>>=1; 22 } 23 if(ans%2==0) printf("X\n"); 24 else printf("Z\n"); 25 } 26 27 return 0; 28 }
T3 “部落冲突”
n个部落,n-1条双向边形成一棵树,相邻部落有时会打仗,有时会停战
打仗时两个部落间的道路不能通行
在某个时间,查询某两个部落是否可以互相到达
看完题,想到这就是一道树链剖分吗,兴高采烈地写了一遍。结果90分,被卡常了一个点(悲伤)
正解:树上差分!
每一次两个点间的路径拆成两个点分别到lca的路径
维护一下每个点到根节点的链中有几条路打仗
判断是就看S(u)+S(v)-2*S(lca(u,v))是否为零就好啦
怎么维护呢?
按dfs序来,对于每一条打仗的边,从进这个点的时间戳开始+1,出这个点的时间戳开始-1,维护前缀和
边边点点的要想清楚!!!
前缀和用树状数组维护,每次查询logn
代码:
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 5 const int MAXN = 300005; 6 struct node{ 7 int v; 8 node *next; 9 }pool[2*MAXN],*h[MAXN]; 10 int cnt; 11 void addedge(int u,int v){ 12 node *p=&pool[++cnt],*q=&pool[++cnt]; 13 p->v=v;p->next=h[u];h[u]=p; 14 q->v=u;q->next=h[v];h[v]=q; 15 } 16 17 int vis[MAXN],fa[MAXN],dep[MAXN],size[MAXN],son[MAXN]; 18 int top[MAXN],rk[MAXN],w[MAXN],tot; 19 void dfs1(int u){ 20 int v,sonnum=0,Mson=0; 21 size[u]=1; 22 vis[u]=1; 23 for(node *p=h[u];p;p=p->next){ 24 v=p->v; 25 if(!vis[v]){ 26 fa[v]=u; 27 dep[v]=dep[u]+1; 28 dfs1(v); 29 size[u]+=size[v]; 30 if(size[v]>sonnum) sonnum=size[v],Mson=v; 31 } 32 } 33 son[u]=Mson; 34 } 35 void dfs2(int u){ 36 int v; 37 v=son[u]; 38 if(v){ 39 top[v]=top[u]; 40 rk[v]=++tot; 41 dfs2(v); 42 } 43 for(node *p=h[u];p;p=p->next){ 44 v=p->v; 45 if(fa[v]==u && v!=son[u]){ 46 top[v]=v; 47 rk[v]=++tot; 48 dfs2(v); 49 } 50 } 51 w[u]=++tot; 52 } 53 int lca(int x,int y){ 54 int f1=top[x],f2=top[y]; 55 while(f1!=f2){ 56 if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y); 57 x=fa[f1];f1=top[x]; 58 } 59 if(dep[x]>dep[y]) swap(x,y); 60 return x; 61 } 62 63 struct Bit{ 64 int c[MAXN*2]; 65 int lowbit(int x){ 66 return x&(-x); 67 } 68 int sum(int x){ 69 int ret=0; 70 while(x>0){ 71 ret+=c[x]; 72 x-=lowbit(x); 73 } 74 return ret; 75 } 76 void add(int x,int y){ 77 while(x<=MAXN*2){ 78 c[x]+=y; 79 x+=lowbit(x); 80 } 81 } 82 }d; 83 84 int hisnum,his[MAXN]; 85 86 int main() 87 { 88 int n,m,x,y,i,l,f; 89 char ch; 90 scanf("%d%d",&n,&m); 91 for(i=1;i<n;i++) scanf("%d%d",&x,&y),addedge(x,y); 92 93 dep[1]=1;dfs1(1); 94 top[1]=1;rk[1]=1;tot=1;dfs2(1); 95 96 for(i=1;i<=m;i++){ 97 cin>>ch; 98 if(ch==‘C‘){ 99 scanf("%d%d",&x,&y); 100 if(y==fa[x]) swap(x,y); 101 his[++hisnum]=y; 102 d.add(rk[y],1); 103 d.add(w[y],-1); 104 } 105 else if(ch==‘U‘){ 106 scanf("%d",&x); 107 y=his[x]; 108 d.add(rk[y],-1); 109 d.add(w[y],1); 110 } 111 else{ 112 scanf("%d%d",&x,&y); 113 l=lca(x,y); 114 f=d.sum(rk[x])+d.sum(rk[y])-2*d.sum(rk[l]); 115 if(f==0) printf("Yes\n"); 116 else printf("No\n"); 117 } 118 } 119 120 // system("pause"); 121 return 0; 122 }
总结:
1.不要把思路老局限在一个东西上,比如第一题
2.灵活变通,不要学死
比如,树上差分也可以完成许多树链剖分可完成的东西啊!
3.前缀和是一个好东西,要学会用它
4.差分也是个好东西,要学会用它
5.写完代码后,记得修改一下耗时多的地方,再将代码完美一下。不要像第二题一样……(好心痛)
6.学会分析复杂度
第一题后面那些查询用前缀和我竟没想到……66
7.注意开long long
以上是关于2017-10-28 noip模拟赛by WISCO 信息组的主要内容,如果未能解决你的问题,请参考以下文章
C. 「NOIP2021模拟赛 By ZJ C」自然溢出 题解
2014-5-10 NOIP模拟赛 by coolyangzc