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 namespaceView Codestd; 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 }View Code
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 }View Code
總結:
1.不要把思路老局限在一個東西上,比如第一題
2.靈活變通,不要學死
比如,樹上差分也可以完成許多樹鏈剖分可完成的東西啊!
3.前綴和是一個好東西,要學會用它
4.差分也是個好東西,要學會用它
5.寫完代碼後,記得修改一下耗時多的地方,再將代碼完美一下。不要像第二題一樣……(好心痛)
6.學會分析復雜度
第一題後面那些查詢用前綴和我竟沒想到……66
7.註意開long long
2017-10-28 noip模擬賽by WISCO 信息組