2020暑假集訓做題記錄——數據結構

題目質量很贊。這麼多道數據結構,卻不全是很裸的板子,並且帶有卡常題

 

A:模擬

WA了好幾發,賊蠢的題,非負整數是個坑

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N1 2000050
 5 using namespace std;
 6 
 7 int T,n;
 8 char str[N1];
 9 
10 int main()
11 {
12     scanf("%d",&T);
13     while(T--)
14     {
15         scanf("%d",&n);
16         if(!n){ puts("0"); continue; }
17         scanf("%s",str+1);
18         int cnt=0,tot=0;
19         for(int i=1;i<=n;i++)
20         {
21             if(str[i]=='('){
22                 cnt++;
23             }else{
24                 if(cnt>0) cnt--, tot+=2;
25             }
26         }
27         printf("%d\n",n-tot);
28     }
29     return 0;
30 }
View Code

 

B:圖論

a[i]向i連邊,由於每個點入度都是1,發現圖中每個連通塊都是基環樹,並且只能是環向外有出邊。所以連通塊個數就是答案

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N1 1000050
 5 #define M1 N1*2
 6 using namespace std;
 7 
 8 struct Edge{
 9 int to[M1],nxt[M1],head[N1],cte;
10 void ae(int u,int v)
11 {cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte; }
12 }e;
13 int T,n;
14 int a[N1],used[N1];
15 void dfs(int u)
16 {
17     used[u]=1;
18     for(int j=e.head[u];j;j=e.nxt[j])
19     {
20         int v=e.to[j]; if(used[v]) continue;
21         dfs(v);
22     }
23 }
24 
25 int main()
26 {
27     scanf("%d",&n);
28     int ans=0;
29     for(int i=1;i<=n;i++) 
30     {
31         scanf("%d",&a[i]);
32         if(i!=a[i]) e.ae(a[i],i), e.ae(i,a[i]);
33     }
34     for(int i=1;i<=n;i++) 
35     {
36         if(!used[i]) dfs(i), ans++;
37     }
38     printf("%d\n",ans);
39     return 0;
40 }
View Code

 

C:樹狀數組

設s[i]是前綴和。考慮暴力做法:枚舉i,j(i>j>=0),統計滿足s[i]-s[j]<=k(i-j)的i,j對數就行了。正解就挪一下式子,把i,j移到兩側,得到s[i]-ki<=s[j]-kj。所以預處理出所有s[i]-ki,離散一下用樹狀數組維護就行。不想離散就動態開點線段樹2333。統計出來以後gcd一下就行。注意離散化的時候從第2位開始賦離散後的值

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N1 200010
 5 #define M1 N1*2
 6 #define ll long long 
 7 using namespace std;
 8 
 9 ll gcd(ll a,ll b)
10 {
11     if(!b) return a;
12     return gcd(b,a%b);
13 }
14 int n,m; ll K;
15 int a[N1],pos[N1];
16 ll sa[N1];
17 
18 struct Bit{
19 ll sum[N1];
20 void upd(int x,int w){
21     for(int i=x;i<=m;i+=i&(-i)) sum[i]+=w;
22 }
23 ll query(int x)
24 {
25     ll ans=0;
26     for(int i=x;i>0;i-=i&(-i)) ans+=sum[i];
27     return ans;
28 }
29 }s;
30 struct node{
31 int id; ll w;
32 }sp[N1];
33 int cmp(node a1,node a2)
34 {
35     if(a1.w!=a2.w) return a1.w<a2.w;
36     return a1.id<a2.id; 
37 }
38 
39 int main()
40 {
41     freopen("a.txt","r",stdin);
42     scanf("%d%lld",&n,&K);
43     for(int i=1;i<=n;i++) scanf("%d",&a[i]), sa[i]=sa[i-1]+a[i];
44     for(int i=1;i<=n;i++) sp[i].w=sa[i]-K*i, sp[i].id=i;
45     sp[n+1]=(node){0,0};
46     sort(sp+1,sp+n+2,cmp); 
47     m=1, pos[sp[1].id]=m;
48     for(int i=2;i<=n+1;i++)
49     {
50         if(sp[i-1].w!=sp[i].w) m++;
51         pos[sp[i].id]=m;
52     }
53     s.upd(pos[n],1); ll ans=0;
54     for(int i=n-1;i>=0;i--)
55     {
56         ans+=s.query(pos[i]);
57         s.upd(pos[i],1);
58     }
59     // printf("%lld\n",ans);
60     ll t1=ans,t2=1ll*(n+1)*n/2, g=gcd(t1,t2);
61     printf("%lld/%lld\n",t1/g,t2/g);
62     return 0;
63 }
View Code

 

D:線段樹

很普通的線段樹題,我還卡了將近40min,菜爆了

線段樹維護一下?記錄f[rt][0]表示左側餘下的)數量,f[rt][1]表示右側餘下(的數量,這兩個變量用於判斷括號序列是否合法,它們向上合併的式子並不難。再考慮最大嵌套怎麼求,考慮暴力做法,打差分,把(當成+1,)當成-1,求一次前綴和,因爲保證括號序列合法,求完以後序列元素的最大值就是最大嵌套層數。把這個做法扔線段樹裏就可以動態了!當前區間的答案就是max(左區間答案,左區間前綴和+右區間答案)。

左側多餘的)和右側多餘的(不必特殊討論,因爲它們遲早得被匹配上,假設左右都給補全就行。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N1 1000005
 5 #define M1 N1*4
 6 #define ll long long 
 7 using namespace std;
 8 
 9 struct SEG{
10 int f[M1][2],sum[M1],ma[M1];
11 #define ls rt<<1
12 #define rs rt<<1|1
13 void pushup(int rt)
14 {
15     f[rt][0]=f[ls][0]+max(f[rs][0]-f[ls][1],0); //left remain ) -1
16     f[rt][1]=f[rs][1]+max(f[ls][1]-f[rs][0],0); //right remain ( 1
17     sum[rt]=sum[ls]+sum[rs];
18     ma[rt]=max(ma[ls],sum[ls]+ma[rs]);
19 }
20 void upd(int x,int l,int r,int rt,int w)
21 {
22     if(l==r){
23         if(w==-1) f[rt][0]=1, f[rt][1]=0;  // )
24         if(w==0)  f[rt][0]=0, f[rt][1]=0;  // *
25         if(w==1)  f[rt][0]=0, f[rt][1]=1;  // (
26         sum[rt]=w, ma[rt]=w; return; 
27     }
28     int mid=(l+r)>>1;
29     if(x<=mid) upd(x,l,mid,ls,w);
30     else upd(x,mid+1,r,rs,w);
31     pushup(rt);
32 }
33 #undef ls
34 #undef rs
35 }s;
36 
37 int check()
38 {
39     if(s.sum[1] || s.f[1][0] || s.f[1][1]) return -1;
40     return s.ma[1];
41 }
42 
43 int n,ma,mi,m;
44 char comd[N1],str[N1];
45 
46 int main()
47 {
48     freopen("a.txt","r",stdin);
49     scanf("%d",&n); 
50     if(!n) return 0;
51     scanf("%s",comd+1);
52     int tmp=0,pos,ans;
53     ma=0,mi=0;
54     for(int i=1;i<=n;i++) 
55         if(comd[i]=='R') tmp++, ma=max(ma,tmp);
56         else if(comd[i]=='L') tmp--, mi=min(mi,tmp);
57     pos=1-mi; m=ma-mi+1;
58     for(int i=1;i<=n;i++)
59     {
60         if(comd[i]=='L') pos--;
61         else if(comd[i]=='R') pos++;
62         else if(comd[i]=='(') s.upd(pos,1,m,1,1);
63         else if(comd[i]==')') s.upd(pos,1,m,1,-1);
64         else s.upd(pos,1,m,1,0);
65         ans=check();
66         printf("%d ",ans);
67     }
68     puts("");
69     return 0;
70 }
View Code

 

E:二維RMQ維護gcd

板子題,看到n=500以爲$O(n^{3}+n^{2}log^{2}n)$能卡過,結果一直T,咋卡常也沒用,數據很強。會寢室路上突然想到RMQ可以拓展到二維,枚舉每個點再二分答案就行了,可以優化成$O(n^{2}logn+n^{2}log^{2}n)$。回去寫了一發,終於過了

 1 #include <ctime>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #define N1 505
 6 #define ll long long 
 7 using namespace std;
 8 
 9 inline int gcd(int x,int y)
10 {
11     if(!y) return x;
12     return gcd(y,x%y);
13 }
14 template <typename _T> void read(_T &ret)
15 {
16     ret=0; _T fh=1; char c=getchar();
17     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
18     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
19     ret=ret*fh;
20 }
21 
22 int n,m,ma;
23 int a[N1][N1],lg[N1];
24 int g[N1][N1][12];
25 int qg(int x,int y,int len)
26 {
27     return gcd( gcd( g[x][y][lg[len]],g[x][y+len-1-(1<<lg[len])+1][lg[len]]) , gcd(g[x+len-1-(1<<lg[len])+1][y][lg[len]], g[x+len-1-(1<<lg[len])+1][y+len-1-(1<<lg[len])+1][lg[len]]) ) ;
28 }
29 
30 int main()
31 {
32     scanf("%d%d",&n,&m);
33     clock_t st,ed;
34     st=clock();
35     for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) read(a[i][j]);
36     ma=max(n,m); lg[1]=0;
37     int ans=0,num=0;
38     for(int i=2;i<=ma;i++) lg[i]=lg[i>>1]+1;
39     for(int i=1;i<=n;i++) 
40     {
41         for(int j=1;j<=m;j++) g[i][j][0]=a[i][j];
42     }
43     for(int k=1;k<=lg[ma];k++)
44     for(int i=1;i+(1<<k)-1<=n;i++) 
45     {
46         for(int j=1;j+(1<<k)-1<=m;j++) 
47         {
48             g[i][j][k]=gcd( gcd(g[i][j][k-1],g[i][j+(1<<(k-1))][k-1]) , gcd(g[i+(1<<(k-1))][j][k-1],g[i+(1<<(k-1))][j+(1<<(k-1))][k-1]) );
49         }
50     }
51     int tmp,l,r,mid,k;
52     for(int i=1;i<=n;i++)
53     for(int j=1;j<=m;j++)
54     {
55         k=0, l=1, r=min(min(m-j,j-1),min(n-i,i-1));
56         while(l<=r)
57         {
58             mid=(l+r)>>1; tmp=qg(i-mid,j-mid,2*mid+1);
59             if(tmp%a[i][j]==0) k=mid, l=mid+1;
60             else r=mid-1;
61         }
62         if((2*k+1)*(2*k+1)>ans) ans=(2*k+1)*(2*k+1), num=1;
63         else if((2*k+1)*(2*k+1)==ans) num++;
64     }
65     printf("%d\n%d\n",ans,num);
66     return 0;
67 }
View Code

 

F:貪心+set 好題

考慮一條鏈的情況,搞個set記錄現在有哪些傭兵可用,再用田忌賽馬策略。對於每個任務,找後繼(第一個比需要的能力值大的忍者),如果沒有後繼,就把最次的傭兵安排上。

環怎麼辦。。。卡了1h多想不出棄療看題解去了

看了byaidu學長的題解,原來可以斷環??!

如果傭兵和任務是一一映射,那麼答案唯一,特判一下就好

對於其他的情況,會有的任務有很多傭兵,那麼它之後的幾個任務的傭兵都可以由它提供剩餘的傭兵,它們之間可以互相影響。那麼可能相互影響的任務一定是環上連續的一段。如果這一段的任務數量>1,那麼最後一個肯定是空任務結尾,且它和前面的任務都不能影響後面的任務。整個序列都是一段的情況依然成立

有了上面的結論,把環給轉一下,就可以化成鏈,捨去尾對頭的影響了!

用鏈表實現任務的添加和刪除,用set找後繼

另外vector是真不快

  1 #include <set>
  2 #include <ctime>
  3 #include <vector>
  4 #include <cstdio>
  5 #include <cstring>
  6 #include <algorithm>
  7 #define N1 500005
  8 #define ll long long 
  9 #define its set<int>::iterator
 10 #define itv vector<int>::iterator
 11 using namespace std;
 12 
 13 template <typename _T> void read(_T &ret)
 14 {
 15     ret=0; _T fh=1; char c=getchar();
 16     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
 17     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
 18     ret=ret*fh;
 19 }
 20 int n;
 21 int id[N1],a[N1],b[N1],num[N1];
 22 struct node{
 23     int l,r;
 24 }lst[N1];
 25 struct Edge{
 26 int to[N1],nxt[N1],head[N1],cte;
 27 void ae(int u,int v)
 28 { cte++; to[cte]=v, nxt[cte]=head[u], head[u]=cte; }
 29 }e;
 30 void pre_lst()
 31 {
 32     lst[1].l=n, lst[1].r=2;
 33     for(int i=2;i<n;i++) lst[i].l=i-1,lst[i].r=i+1;
 34     lst[n].l=n-1, lst[n].r=1;
 35 }
 36 int del(int i)
 37 {
 38     lst[lst[i].l].r=lst[i].r;
 39     lst[lst[i].r].l=lst[i].l;
 40     return lst[i].r;
 41 }
 42 int find_st()
 43 {
 44     pre_lst();
 45     int tot=n,i=1,res,st;
 46     while(tot)
 47     {
 48         if(!num[i]){ i=lst[i].r; continue; }
 49         res=num[i];
 50         while(res)
 51         {
 52             i=del(i); res--; tot--; 
 53             if(res==0) break;
 54             res+=num[i];
 55         }
 56     }
 57     if(!tot) st=(i==n?1:i+1); else st=i;
 58     return st;
 59 }
 60 set<int>s;
 61 vector<int>mer[N1];
 62 int solve(int i)
 63 {
 64     pre_lst();
 65     int tot=n,j,ans=0;its k;
 66     for(;tot;)
 67     {
 68         // if(!num[i]){ i=lst[i].r; continue; }
 69         for(j=0;j<mer[i].size();j++) s.insert(b[mer[i][j]]);
 70         // for(j=e.head[i];j;j=e.nxt[j]) s.insert(b[e.to[j]]);
 71         if(s.empty()){ i=lst[i].r; continue; }
 72         k=s.upper_bound(a[i]);
 73         if(k==s.end()){
 74             k=s.begin(); s.erase(*k);
 75         }else{
 76             s.erase(*k); ans++;
 77         }
 78         i=del(i), tot--; 
 79     }
 80     return ans;
 81 }
 82 
 83 int main()
 84 {
 85     // freopen("f.txt","r",stdin);
 86     // freopen("a.in","r",stdin);
 87     // freopen("f.out","w",stdout);
 88     clock_t sta,ed,tmp;
 89     sta=clock();
 90     scanf("%d",&n);
 91     for(int i=1;i<=n;i++) read(id[i]), num[id[i]]++;
 92     for(int i=1;i<=n;i++) mer[id[i]].emplace_back(i);
 93     // for(int i=1;i<=n;i++) read(id[i]), num[id[i]]++, e.ae(id[i],i);
 94     for(int i=1;i<=n;i++) read(a[i]); //任務
 95     for(int i=1;i<=n;i++) read(b[i]); //傭兵
 96     int flag=1; int ans=0;
 97     for(int i=1;i<=n;i++) if(num[i]!=1){ flag=0; break; }
 98     if(flag)
 99     {
100         for(int i=1;i<=n;i++)
101         {
102             if(b[i]>a[id[i]]) ans++;
103         }
104         printf("%d\n",ans); 
105         return 0;
106     }
107     int st=find_st();
108     ans=solve(st);
109     printf("%d\n",ans);
110     // ed=clock(); printf("%.7lf s\n",(double)(ed-sta)/CLOCKS_PER_SEC);
111     return 0;
112 }
View Code

 

G:線段樹合併+離散化

線段樹合併板子題,但此題很毒瘤似乎卡空間,於是乎需要離散,把dis[x]和dis[x]+L一起離散2333

線段樹合併又又又又又又又又又又tnnd打錯了,調了30minWA了2發,再打錯我就是sb!

  1 #include <set>
  2 #include <ctime>
  3 #include <vector>
  4 #include <cstdio>
  5 #include <cstring>
  6 #include <algorithm>
  7 #define N1 400005
  8 #define ll long long 
  9 #define its set<int>::iterator
 10 #define itv vector<int>::iterator
 11 using namespace std;
 12 
 13 template <typename _T> void read(_T &ret)
 14 {
 15     ret=0; _T fh=1; char c=getchar();
 16     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
 17     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
 18     ret=ret*fh;
 19 }
 20 struct Edge{
 21 int to[N1],nxt[N1],head[N1],cte; ll val[N1];
 22 void ae(int u,int v,ll w)
 23 {
 24     cte++; to[cte]=v, nxt[cte]=head[u];
 25     head[u]=cte, val[cte]=w;
 26 }
 27 }e;
 28 struct SEG{; //2N*log*2
 29 int sum[N1*40],ls[N1*40],rs[N1*40],root[N1],cnt;
 30 void pushup(int rt){ sum[rt]=sum[ls[rt]]+sum[rs[rt]]; }
 31 void upd(int x,int l,int r,int &rt,int w)
 32 {
 33     if(!rt) rt=++cnt;
 34     // if(cnt>=N1*40) puts("-1");
 35     if(l==r){ sum[rt]+=w; return; }
 36     int mid=(l+r)>>1;
 37     if(x<=mid) upd(x,l,mid,ls[rt],w);
 38     else upd(x,mid+1,r,rs[rt],w);
 39     pushup(rt);
 40 }
 41 int mrg(int r1,int r2)
 42 {
 43     if(!r1||!r2) return r1+r2;
 44     int rt=++cnt;
 45     ls[rt]=mrg(ls[r1],ls[r2]);
 46     rs[rt]=mrg(rs[r1],rs[r2]);
 47     sum[rt]=sum[r1]+sum[r2];
 48     return rt;
 49 }
 50 int query(int L,int R,int l,int r,int rt)
 51 {
 52     if(L<=l&&r<=R) return sum[rt];
 53     int mid=(l+r)>>1,ans=0;
 54     if(L<=mid) ans+=query(L,R,l,mid,ls[rt]);
 55     if(R>mid) ans+=query(L,R,mid+1,r,rs[rt]);
 56     return ans;
 57 }
 58 }s;
 59 int n,m; ll L;
 60 int dis[N1],disl[N1],ans[N1];
 61 struct node{ int id,p; ll w; }bar[N1];
 62 int cmp(node s1,node s2)
 63 { 
 64     if(s1.w!=s2.w) return s1.w<s2.w; 
 65     if(s1.p!=s2.p) return s1.p<s2.p; 
 66     return s1.id<s2.id; 
 67 }
 68 
 69 void dfs1(int u)
 70 {
 71     int j,v;
 72     bar[u].id=u; bar[u+n].id=u; bar[u].p=0; bar[u+n].p=1;
 73     bar[u+n].w=bar[u].w+L;
 74     for(j=e.head[u];j;j=e.nxt[j])
 75     {
 76         v=e.to[j]; 
 77         bar[v].w=bar[u].w+e.val[j]; 
 78         // dis[v]=dis[u]+e.val[j];
 79         dfs1(v);
 80     }
 81 }
 82 void dfs2(int u)
 83 {
 84     int j,v;
 85     s.upd(dis[u],1,m,s.root[u],1);
 86     for(j=e.head[u];j;j=e.nxt[j])
 87     {
 88         v=e.to[j]; 
 89         dfs2(v);
 90         s.root[u]=s.mrg(s.root[u],s.root[v]);
 91     }
 92     if(u==1)
 93         n=n;
 94     ans[u]=s.query(1,disl[u],1,m,s.root[u]);
 95 }
 96 int main()
 97 {
 98     read(n); read(L);
 99     int x; ll y;
100     for(int i=2;i<=n;i++) read(x), read(y), e.ae(x,i,y);
101     dfs1(1);
102     sort(bar+1,bar+2*n+1,cmp); 
103     bar[0].w=-1;
104     for(int i=1;i<=2*n;i++) 
105     {
106         if(bar[i].w!=bar[i-1].w) m++;
107         if(!bar[i].p) dis[bar[i].id]=m;
108         else disl[bar[i].id]=m;
109     }
110     dfs2(1);
111     for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
112     return 0;
113 }
View Code

 

H:分塊

原來想了個拆詢問+主席樹的log方做法,寫了一下發現是假的qwq。

索性暴力分塊。不過複習了分塊也不錯

卡常卡半天我好難嗚嗚嗚,實測塊大小爲250跑的最快。

 1 #include <ctime>
 2 #include <vector>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <algorithm>
 6 #define N1 100005
 7 #define M1 N1*4
 8 #define ll long long 
 9 using namespace std;
10 
11 template <typename _T> void read(_T &ret)
12 {
13     ret=0; _T fh=1; char c=getchar();
14     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
15     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
16     ret=ret*fh;
17 }
18 const ll sq=250;
19 
20 int getid(int x){ return (x-1)/sq+1; }
21 int n,K,m;
22 int a[N1],tag[N1/sq+5],sum[N1/sq+5][N1];
23 void pushdown(int id)
24 {
25     if(!tag[id]) return; 
26     for(int i=(id-1)*sq+1;i<=id*sq;i++)
27     {
28         sum[id][a[i]]--;
29         (a[i]+=tag[id])%=K;
30         sum[id][a[i]]++;
31     }
32     tag[id]=0;
33 }
34 
35 int main()
36 {
37     // freopen("h.txt","r",stdin);
38     freopen("h.in","r",stdin);
39     freopen("h0.out","w",stdout);
40     clock_t sta,ed,tmp;
41     sta=clock();
42     read(n); read(K);
43     for(int i=1;i<=n;i++) read(a[i]), a[i]=(a[i]%K+K)%K;
44     read(m);
45     int l,r,c,d,ans,lid,rid;
46     for(int i=1;i<=n;i++)
47     {
48         sum[getid(i)][a[i]]++;
49     }
50     for(int k=1;k<=m;k++)
51     {
52         read(l); read(r); read(c); read(d); ans=0;
53         c=(c%K+K)%K; d=(d%K+K)%K; 
54         lid=getid(l); rid=getid(r); 
55         pushdown(lid); pushdown(rid);
56         if(lid==rid)
57         {
58             for(int i=l;i<=r;i++)
59             {
60                 if(a[i]==c) ans++;
61                 sum[lid][a[i]]--; (a[i]+=d)%=K; sum[lid][a[i]]++;
62             }
63         }else{
64             for(int i=l;i<=lid*sq;i++) 
65             {
66                 if(a[i]==c) ans++;
67                 sum[lid][a[i]]--; (a[i]+=d)%=K; sum[lid][a[i]]++;
68             }
69             for(int i=lid+1;i<=rid-1;i++) 
70             {
71                 ans+=sum[i][(c-tag[i]+K)%K];
72                 (tag[i]+=d)%=K;
73             }
74             for(int i=(rid-1)*sq+1;i<=r;i++) 
75             {
76                 if(a[i]==c) ans++;
77                 sum[rid][a[i]]--; (a[i]+=d)%=K; sum[rid][a[i]]++;
78             }
79         }
80         printf("%d\n",ans);
81     }
82     // ed=clock(); printf("%.7lf s\n",(double)(ed-sta)/CLOCKS_PER_SEC);
83     return 0;
84 }
View Code

 

I:種類並查集

竟然不會了555我太菜了,複習一波並查集拓展做法√

種類並查集板子題,和NOI2001 食物鏈一個做法。開三倍的n記錄點和點之間的關係

並查集不能維護刪邊,所以我們倒着遍歷詢問變成了加邊。注意刪除同一個操作時需要在第一次(倒着看就是最後一次)刪除的時候加邊!

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int N1=100005;
 6 const int M1=1000005;
 7 
 8 template <typename _T> void read(_T &ret)
 9 {
10     ret=0; _T fh=1; char c=getchar();
11     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
12     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
13     ret=ret*fh;
14 }
15 
16 int n,K,m;
17 int fa[N1*3];
18 // vector<int>son[N1];
19 int used[M1],prt[M1],cnt;
20 struct comd{ int p,x,y; }des[M1],ques[M1];
21 
22 int findfa(int x)
23 {
24     int y=x,t;
25     while(fa[y]!=y) y=fa[y];
26     while(fa[x]!=y){ t=fa[x], fa[x]=y, x=t; }
27     return y;
28 }
29 int mrg(int x,int y)
30 {
31     int fx=findfa(x), fy=findfa(y);
32     if(fx==fy) return fx; 
33     fa[fy]=fx;
34     return fx;
35 }
36 int check(int x,int y)
37 {
38     if(findfa(x)==findfa(y)) return 1;
39     return 0;
40 }
41 
42 
43 int main()
44 {
45     freopen("a.in","r",stdin);
46     scanf("%d%d%d",&n,&K,&m);
47     char str[10];
48     for(int i=1;i<=K;i++) read(des[i].p), read(des[i].x), read(des[i].y);
49     for(int i=1;i<=m;i++)
50     {
51         scanf("%s",str);
52         if(str[0]=='q'){
53             ques[i].p=1; read(ques[i].x); read(ques[i].y);
54         }else{
55             ques[i].p=2; read(ques[i].x); 
56             if(!used[ques[i].x]) used[ques[i].x]=i;
57         }
58     }
59     for(int i=1;i<=3*n;i++) fa[i]=i;
60     int p,x,y,ans;
61     for(int i=1;i<=K;i++)
62     {
63         if(used[i]) continue;
64         p=des[i].p, x=des[i].x, y=des[i].y;
65         if(p==1){
66             mrg(x,y); mrg(x+n,y+n); mrg(x+2*n,y+2*n);
67         }else{
68             mrg(x+n,y); mrg(x+2*n,y+n); mrg(x,y+2*n);
69         }
70     }
71     for(int j=m,i;j>=1;j--)
72     {
73         if(ques[j].p==1){
74             x=ques[j].x, y=ques[j].y;
75             if(check(x,y)&&check(x+n,y+n)&&check(x+2*n,y+2*n)) ans=0;
76             else if(check(x+n,y)&&check(x+2*n,y+n)&&check(x,y+2*n)) ans=1;
77             else if(check(y+n,x)&&check(y+2*n,x+n)&&check(y,x+2*n)) ans=2;
78             else ans=3;
79             prt[++cnt]=ans;
80             // printf("%d\n",ans);
81         }else{       
82             i=ques[j].x;
83             if(used[i]!=j) continue; 
84             p=des[i].p, x=des[i].x, y=des[i].y;
85             if(p==1){
86                 mrg(x,y); mrg(x+n,y+n); mrg(x+2*n,y+2*n);
87             }else{
88                 mrg(x+n,y); mrg(x+2*n,y+n); mrg(x,y+2*n);
89             }
90         }
91     }
92     for(int i=cnt;i>=1;i--) printf("%d\n",prt[i]);
93     return 0;
94 }
View Code

 

J:點分治+01Trie

把點分治給忘了我太弱了555,複習一波點分治板子√

原來想了一個01Trie啓發式合併,寫了一下發現又是假做法。。

統計鏈性質的問題多半都能用點分治搞呢

對於每個點的點分樹,暴力上01trie求解答案就行了= =

怎麼統計?按位分0/1討論,r的答案減掉l-1的答案,注意l-1可能小於0

轉化成如下問題:

已知數列$a$和數列$b$,要求有多少對$ai^bj\le maxn$

對$a$建成$01trie$,一次遍歷$b$中每個元素求答案

按位從高到低討論,也就是在$01trie$上走

如果$maxn$某一位是1:

$bj$是0,$ai$這一位填0一定成立,答案加上$sum[ch(x,0)]$。填1不一定成立,需要看後面的位,走向1兒子

$bj$是1,$ai$這一位填1一定成立,答案加上$sum[ch(x,1)]$。填0不一定成立,需要看後面的位,走向0兒子

如果$maxn$某一位是0:

$bj$是0,$ai$這一位只能填0而且需要看後面的位,走向0兒子

$bj$是1,$ai$這一位只能填1而且需要看後面的位,走向1兒子

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #define ll long long
  5 using namespace std;
  6 const int maxn=29;
  7 const int N1=100005;
  8 const int M1=3100005;
  9 template <typename _T> void read(_T &ret)
 10 {
 11     ret=0; _T fh=1; char c=getchar();
 12     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
 13     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
 14     ret=ret*fh;
 15 }
 16 
 17 int n,l,r,de;
 18 struct Edge{
 19 int to[N1*2],nxt[N1*2],head[N1],cte;
 20 void ae(int u,int v)
 21 {cte++; to[cte]=v, nxt[cte]=head[u], head[u]=cte; }
 22 }e;
 23 struct Trie{
 24 int sum[M1],ch[M1][2],cnt;
 25 void clr()
 26 {
 27     for(int i=1;i<=cnt;i++) sum[i]=ch[i][0]=ch[i][1]=0;
 28     cnt=1;
 29 }
 30 void upd(int w)
 31 {
 32     int p,x=1;
 33     for(int i=maxn;i>=0;i--)
 34     {
 35         p=(w>>i)&1; 
 36         if(!ch[x][p]) ch[x][p]=++cnt; 
 37         x=ch[x][p]; sum[x]++;
 38     }
 39 }
 40 int query(int w,int m)
 41 {
 42     int ans=0,x=1;
 43     if(m<0) return 0;
 44     for(int i=maxn;i>=0;i--)
 45     {
 46         if((m>>i)&1){
 47             if((w>>i)&1) ans+=sum[ch[x][1]], x=ch[x][0]; // w 1  r 1
 48             else ans+=sum[ch[x][0]], x=ch[x][1]; // w 0  r 1
 49             if(!i) ans+=sum[x];
 50         }else{
 51             if((w>>i)&1) x=ch[x][1]; // w 1  r 0
 52             else x=ch[x][0]; // w 0  r 0
 53             if(!i) ans+=sum[x];
 54         }
 55     }
 56     return ans;
 57 }
 58 }t;
 59 
 60 int used[N1],sz[N1],ms[N1],tsz,G;
 61 void gra(int u,int dad)
 62 {
 63     sz[u]=1; ms[u]=0;
 64     for(int j=e.head[u];j;j=e.nxt[j])
 65     {
 66         int v=e.to[j];
 67         if(v==dad||used[v]) continue; 
 68         gra(v,u); sz[u]+=sz[v];
 69         ms[u]=max(ms[u],sz[v]);
 70     }
 71     ms[u]=max(ms[u],tsz-sz[u]);
 72     if(ms[u]<ms[G]) G=u;
 73 }
 74 int a[N1],sa0[N1],sa1[N1];
 75 void dfs0(int u,int dad)
 76 {
 77     for(int j=e.head[u];j;j=e.nxt[j])
 78     {
 79         int v=e.to[j]; if(v==dad||used[v]) continue; 
 80         sa0[v]=sa0[u]^a[v]; sa1[v]=sa1[u]^a[v];
 81         dfs0(v,u);
 82     }
 83 }
 84 ll dfs1(int u,int dad) //calc
 85 {
 86     ll ans=t.query(sa1[u],r);
 87     ans-=t.query(sa1[u],l-1);
 88     for(int j=e.head[u];j;j=e.nxt[j])
 89     {
 90         int v=e.to[j];
 91         if(v==dad||used[v]) continue; 
 92         ans+=dfs1(v,u);
 93     }
 94     return ans;
 95 }
 96 void dfs2(int u,int dad) //update in trie
 97 {
 98     t.upd(sa0[u]);
 99     for(int j=e.head[u];j;j=e.nxt[j])
100     {
101         int v=e.to[j];
102         if(v==dad||used[v]) continue; 
103         dfs2(v,u);
104     }
105 }
106 ll calc(int u)
107 {
108     sa0[u]=a[u]; sa1[u]=0; dfs0(u,-1);
109     t.upd(sa0[u]);
110     ll ans=0;
111     for(int j=e.head[u];j;j=e.nxt[j])
112     {
113         int v=e.to[j]; if(used[v]) continue; 
114         ans+=dfs1(v,u); 
115         dfs2(v,u);
116     }
117     t.clr();
118     return ans;
119 }
120 ll main_dfs(int u)
121 {
122     ll ans=0;
123     used[u]=1; ans+=calc(u);
124     for(int j=e.head[u];j;j=e.nxt[j])
125     {
126         int v=e.to[j]; if(used[v]) continue; 
127         G=0; tsz=sz[v]; gra(v,u);
128         ans+=main_dfs(G);
129     }
130     return ans;
131 }
132 
133 int main()
134 {
135     freopen("a.in","r",stdin);
136     int x,y;
137     scanf("%d%d%d",&n,&l,&r);
138     for(int i=1;i<=n;i++) read(a[i]);
139     for(int i=1;i<n;i++) read(x), read(y), e.ae(x,y), e.ae(y,x);
140     ms[0]=tsz=n; gra(1,-1); gra(G,-1);
141     t.clr();
142     ll ans=main_dfs(G);
143     printf("%lld\n",ans);
144     return 0;
145 }
View Code

 

K:貪心+單調棧

把問題想象成把一個個元素放到後面填上去

如果當前元素$a[i]$小於答案序列的最後一個元素$b[j]$,這個位置放$a[i]$會更優秀,但必須保證序列合法。

那麼如果$b[j]$可以被刪除,僅當$i$小於$b[j]$在原序列中最後一次出現的位置

如果$a[i]$大於等於$b[j]$就沒有刪除的必要了

可能有多個$b[j]$被同時刪除,用單調棧維護答案序列即可

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define ll long long
 5 using namespace std;
 6 const int N1=1000005;
 7 
 8 template <typename _T> void read(_T &ret)
 9 {
10     ret=0; _T fh=1; char c=getchar();
11     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
12     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
13     ret=ret*fh;
14 }
15 
16 int n,K;
17 int a[N1],la[N1],used[N1];
18 int stk[N1],tp;
19 
20 int main()
21 {
22     freopen("a.in","r",stdin);
23     scanf("%d%d",&n,&K);
24     for(int i=1;i<=n;i++) read(a[i]), la[a[i]]=i;
25     for(int i=1;i<=K;i++)
26     {
27         if(!la[i]){
28             puts("Kanade"); return 0;
29         }
30     }
31     for(int i=1;i<=n;i++)
32     {
33         if(used[a[i]]) continue;
34         while(tp>0&&a[i]<stk[tp])
35         {
36             if(i<la[stk[tp]]) used[stk[tp]]=0, stk[tp]=0, tp--;
37             else break;
38         }
39         stk[++tp]=a[i]; used[a[i]]=1;
40     }
41     for(int i=1;i<=tp;i++) printf("%d ",stk[i]);
42     puts("");
43     return 0;
44 }
View Code

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章