1. 程式人生 > >2017-10-28 noip模擬賽by WISCO 信息組

2017-10-28 noip模擬賽by WISCO 信息組

卡常 有一個 struct 根節點 space pau 證明 class 數學

第一次做模擬賽,自我感覺良好(大概是這套題比較簡單)

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 }
View Code

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 信息組