題目質量很贊。這麼多道數據結構,卻不全是很裸的板子,並且帶有卡常題
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }