D1T1:异或粽子
显然令b[]为a[]的前缀和,那么就是在b[]中任取两数异或,求异或结果前k大和。
于是暴力$O(n^2)$显然,60pts。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 typedef long long ll; 5 using namespace std; 6 7 const int N=2000010; 8 int n,k,tot; 9 ll ans,a[N],b[N],s[N]; 10 11 int main(){ 12 freopen("xor.in","r",stdin); 13 freopen("xor.out","w",stdout); 14 scanf("%d%d",&n,&k); 15 rep(i,1,n) scanf("%lld",&a[i]),b[i]=b[i-1]^a[i]; 16 rep(i,1,n) rep(j,0,i-1) s[++tot]=b[i]^b[j]; 17 sort(s+1,s+tot+1); 18 for (int i=tot; i>=tot-k+1; i--) ans+=s[i]; 19 printf("%lld\n",ans); 20 return 0; 21 }
方法一:类似[NOI2010]超级钢琴
将(l,r,L,R,w)放入堆中,代表现在左端点为l的区间的下一个被考虑的右端点在[L,R]中,其中最大的右端点为r,b[r]^b[l-1]=w。
每次取出堆顶计入答案,再分裂成(l,r1,L,r-1,w1)和(l,r2,r+1,R,w2)分别放入堆中。用可持久化Trie实现对r和w的求值。复杂度$O(n\log n)$,常数较大。
1 #include<queue> 2 #include<cstdio> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=500010,M=N*35; 9 int n,k,nd,rt[N],c[M][2],sz[M],pos[M]; 10 ll ans,a[N],b[N]; 11 struct P{ int l,r,L,R; ll w; }; 12 bool operator <(const P &a,const P &b){ return a.w<b.w; } 13 priority_queue<P>Q; 14 15 void ins(int y,int &x,int w,ll k,int p){ 16 sz[x=++nd]=sz[y]+1; c[x][0]=c[y][0]; c[x][1]=c[y][1]; 17 if (p<0){ pos[x]=w; return; } 18 ins(c[y][(k>>p)&1],c[x][(k>>p)&1],w,k,p-1); 19 } 20 21 int que(int x,int y,ll k,int p){ 22 if (p<0) return pos[y]; 23 int t=(k>>p)&1; 24 if (sz[c[y][t^1]]-sz[c[x][t^1]]) return que(c[x][t^1],c[y][t^1],k,p-1); 25 else return que(c[x][t],c[y][t],k,p-1); 26 } 27 28 int main(){ 29 freopen("xor.in","r",stdin); 30 freopen("xor.out","w",stdout); 31 scanf("%d%d",&n,&k); 32 rep(i,1,n) scanf("%lld",&a[i]),b[i]=b[i-1]^a[i],ins(rt[i-1],rt[i],i,b[i],33); 33 rep(i,1,n){ 34 int r=que(rt[i-1],rt[n],b[i-1],33); 35 Q.push((P){i,r,i,n,b[r]^b[i-1]}); 36 } 37 while (k--){ 38 P x=Q.top(); Q.pop(); ans+=x.w; 39 if (x.r>x.L){ 40 int r=que(rt[x.L-1],rt[x.r-1],b[x.l-1],33); 41 Q.push((P){x.l,r,x.L,x.r-1,b[r]^b[x.l-1]}); 42 } 43 if (x.r<x.R){ 44 int r=que(rt[x.r],rt[x.R],b[x.l-1],33); 45 Q.push((P){x.l,r,x.r+1,x.R,b[r]^b[x.l-1]}); 46 } 47 } 48 printf("%lld\n",ans); 49 return 0; 50 }
方法二:
注意到对于同一个左端点l,右端点一定是从大到小(指b[r]^b[l-1])取的。于是可以将五元组简化为三元(l,k,w),表示左端点下一个被考虑的右端点是第k大的,b[r]^b[l-1]=w。每次取出堆顶计入答案,再将(l,k+1,w')放入堆中。用普通01Trie上求子树和实现找第k大。复杂度$O(n\log n)$,常数较小。
1 #include<queue> 2 #include<cstdio> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=500010,M=N*35; 9 int n,k,nd,x,d[N],c[M][2],sz[M]; 10 ll ans,a[N],b[N]; 11 struct P{ int x; ll v; }; 12 bool operator <(const P &a,const P &b){ return a.v<b.v; } 13 priority_queue<P>Q; 14 15 void ins(ll k){ 16 int x=0; 17 for (int i=33; ~i; i--){ 18 int t=(k>>i)&1; 19 if (!c[x][t]) c[x][t]=++nd; 20 x=c[x][t]; sz[x]++; 21 } 22 } 23 24 ll que(ll k,int p){ 25 int x=0; ll res=0; 26 for (int i=33; ~i; i--){ 27 int t=(k>>i)&1; 28 if (sz[c[x][t^1]]<p) p-=sz[c[x][t^1]],x=c[x][t]; 29 else res|=1ll<<i,x=c[x][t^1]; 30 } 31 return res; 32 } 33 34 int main(){ 35 freopen("xor.in","r",stdin); 36 freopen("xor.out","w",stdout); 37 scanf("%d%d",&n,&k); ins(0); k<<=1; 38 rep(i,1,n) scanf("%lld",&a[i]),b[i]=b[i-1]^a[i],ins(b[i]); 39 rep(i,0,n) Q.push((P){i,que(b[i],d[i]=1)}); 40 while (k--) ans+=Q.top().v,x=Q.top().x,Q.pop(),Q.push((P){x,que(b[x],++d[x])}); 41 printf("%lld\n",ans>>1); 42 return 0; 43 }
D1T2:字符串问题
对A和B分别建点,若Ai支配Bj,则i向j'连边。若Bi是Aj的前缀,则i'向j连边。然后拓扑排序,若有环则输出-1,否则DAG上DP找最长链。
暴力建图$O(n^2)$,40pts。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=2000010,P1=131,P2=20020223; 10 char S[N]; 11 int n,na,nb,len,m,cnt,T,x,y,q[N],la[N],ra[N],lb[N],rb[N]; 12 int pw[N],ind[N],hs[N],h[N],to[N<<1],nxt[N<<1]; 13 ll ans,f[N]; 14 15 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; ind[v]++; } 16 17 int get(int l,int r){ return (hs[r]-1ll*hs[l-1]*pw[r-l+1]%P2+P2)%P2; } 18 19 bool ispre(int x,int y){ 20 if (rb[x]-lb[x]>ra[y]-la[y]) return 0; 21 return get(lb[x],rb[x])==get(la[y],la[y]+(rb[x]-lb[x])); 22 } 23 24 void init(){ cnt=ans=0; rep(i,1,n) h[i]=ind[i]=f[i]=0; } 25 26 int main(){ 27 freopen("string.in","r",stdin); 28 freopen("string.out","w",stdout); 29 pw[0]=1; rep(i,1,200000) pw[i]=1ll*pw[i-1]*P1%P2; 30 for (scanf("%d",&T); T--; ){ 31 init(); scanf("%s",S+1); len=strlen(S+1); 32 rep(i,1,len) hs[i]=(1ll*hs[i-1]*P1+S[i]-'a')%P2; 33 scanf("%d",&na); 34 rep(i,1,na) scanf("%d%d",&la[i],&ra[i]); 35 scanf("%d",&nb); n=na+nb; 36 rep(i,1,nb) scanf("%d%d",&lb[i],&rb[i]); 37 scanf("%d",&m); 38 rep(i,1,m) scanf("%d%d",&x,&y),add(x,y+na); 39 rep(i,1,nb) rep(j,1,na) if (ispre(i,j)) add(i+na,j); 40 int st=0,ed=0; 41 rep(i,1,n) if (!ind[i]) q[++ed]=i; 42 while (st!=ed){ 43 int x=q[++st]; 44 For(i,x){ 45 ind[k=to[i]]--; 46 if (!ind[k]) q[++ed]=k; 47 } 48 } 49 bool flag=0; 50 rep(i,1,n) if (ind[i]){ flag=1; break; } 51 if (flag){ puts("-1"); continue; } 52 for (int j=n; j; j--){ 53 int x=q[j]; 54 For(i,x) f[x]=max(f[x],f[k=to[i]]); 55 f[x]+=(x<=na) ? ra[x]-la[x]+1 : 0; 56 } 57 rep(i,1,na) ans=max(ans,f[i]); 58 printf("%lld\n",ans); 59 } 60 return 0; 61 }
方法一:后缀数组+主席树优化建图
先只考虑所有B都比A短的情况,对A中所有串按字典序排序,那么以Bi为前缀的串在数组中一定是一个连续的区间,于是用线段树优化建图即可。
然后考虑长度问题,将A和B都按长度从小到大排序,然后主席树优化建图即可。
复杂度$O(n\log n)$,常数较大,下面代码有个点一直过不去只能特判。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define mem(a) memset(a,0,sizeof(a)) 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 7 typedef long long ll; 8 using namespace std; 9 10 const int N=200010,M=N*30; 11 char S[N]; 12 ll ans,f[M]; 13 int T,n,nd,na,nb,m,u,v,cnt,rt,len; 14 int id[N],c[N],rk[N],he[N],st1[N][19],st2[N][19]; 15 int h[M],to[M<<1],nxt[M<<1],ls[M],rs[M],q[M],ind[M],x[N],y[N],sa[N]; 16 struct P{ int l,r,id; }a[N],b[N],t[N],tt[N]; 17 18 bool cmp(const P &a,const P &b){ return a.r-a.l>b.r-b.l; } 19 bool operator <(const P &a,const P &b){ return rk[a.l]<rk[b.l]; } 20 21 void add(int u,int v){ if (!v) return; to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; ind[v]++; } 22 23 bool Cmp(int a,int b,int l){ return a+l<=n && b+l<=n && y[a]==y[b] && y[a+l]==y[b+l]; } 24 25 int rd(){ 26 int x=0; char ch=getchar(); 27 while (ch<'0' || ch>'9') ch=getchar(); 28 while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); 29 return x; 30 } 31 32 void buildsa(int m){ 33 rep(i,0,m) c[i]=0; 34 rep(i,1,n) c[x[i]=S[i]-'a']++; 35 rep(i,1,m) c[i]+=c[i-1]; 36 for (int i=n; i; i--) sa[c[x[i]]--]=i; 37 for (int k=1,p=0; p<n; k<<=1,m=p){ 38 p=0; 39 rep(i,n-k+1,n) y[++p]=i; 40 rep(i,1,n) if (sa[i]>k) y[++p]=sa[i]-k; 41 rep(i,0,m) c[i]=0; 42 rep(i,1,n) c[x[y[i]]]++; 43 rep(i,1,m) c[i]+=c[i-1]; 44 for (int i=n; i; i--) sa[c[x[y[i]]]--]=y[i]; 45 rep(i,1,n) y[i]=x[i]; x[sa[p=1]]=1; 46 rep(i,2,n) x[sa[i]]=Cmp(sa[i],sa[i-1],k) ? p : ++p; 47 } 48 } 49 50 void getst(){ 51 rep(i,1,n) rk[sa[i]]=i; 52 int k=0; 53 rep(i,1,n){ 54 for (int j=sa[rk[i]-1]; i+k<=n && j+k<=n && S[i+k]==S[j+k]; k++); 55 he[rk[i]]=k; if (k) k--; 56 } 57 rep(i,1,n) st1[i][0]=he[i],st2[i][0]=he[i+1]; 58 rep(i,1,18) rep(j,1,n-(1<<i)+1) 59 st1[j][i]=min(st1[j][i-1],st1[j+(1<<(i-1))][i-1]); 60 rep(i,1,18) rep(j,(1<<i)+1,n) 61 st2[j][i]=min(st2[j][i-1],st2[j-(1<<(i-1))][i-1]); 62 } 63 64 void ins(int y,int &x,int L,int R,int k){ 65 if (L==R){ x=L; return; } 66 x=++nd; ls[x]=ls[y]; rs[x]=rs[y]; int mid=(L+R)>>1; 67 if (k<=mid) ins(ls[y],ls[x],L,mid,k); 68 else ins(rs[y],rs[x],mid+1,R,k); 69 add(x,ls[x]); add(x,rs[x]); 70 } 71 72 void Add(int x,int L,int R,int l,int r,int k){ 73 if (L==l && r==R){ add(k,x); return; } 74 int mid=(L+R)>>1; 75 if (r<=mid) Add(ls[x],L,mid,l,r,k); 76 else if (l>mid) Add(rs[x],mid+1,R,l,r,k); 77 else Add(ls[x],L,mid,l,mid,k),Add(rs[x],mid+1,R,mid+1,r,k); 78 } 79 80 int find(int x){ 81 int L=1,R=na+1; 82 while (L<R){ 83 int mid=(L+R)>>1; 84 if (rk[t[mid].l]<x) L=mid+1; else R=mid; 85 } 86 return L; 87 } 88 89 void init(){ 90 cnt=rt=0; 91 rep(i,1,n) h[i]=f[i]=ind[i]=ls[i]=rs[i]=0; 92 rep(i,1,len) y[i]=0; 93 rep(i,1,len) rep(j,1,18) st1[i][j]=st2[i][j]=0; 94 } 95 96 int main(){ 97 freopen("string.in","r",stdin); 98 freopen("string.out","w",stdout); 99 for (scanf("%d",&T); T--; ){ 100 init(); scanf("%s",S+1); len=n=strlen(S+1); int p=0; 101 na=rd(); 102 rep(i,1,na) a[i].l=rd(),a[i].r=rd(),a[i].id=i,t[i]=tt[i]=a[i]; 103 nb=rd(); 104 rep(i,1,nb) b[i].l=rd(),b[i].r=rd(),b[i].id=i; 105 buildsa(30); getst(); nd=na+nb; sort(t+1,t+na+1); 106 rep(i,1,na) id[t[i].id]=i; 107 m=rd(); 108 rep(i,1,m) u=rd(),v=rd(),add(id[u],v+na); 109 sort(a+1,a+na+1,cmp); sort(b+1,b+nb+1,cmp); 110 rep(i,1,nb){ 111 while (p<n && a[p+1].r-a[p+1].l>=b[i].r-b[i].l) ins(rt,rt,1,na,id[a[++p].id]); 112 int x=rk[b[i].l],l=b[i].r-b[i].l+1; 113 for (int j=18; ~j; j--) if (st1[x+1][j]>=l) x+=1<<j; 114 int sr=find(x+1)-1; 115 x=rk[b[i].l]; 116 for (int j=18; ~j; j--) if (st2[x-1][j]>=l) x-=1<<j; 117 int sl=find(x); 118 if (sl>sr) continue; 119 Add(rt,1,na,sl,sr,b[i].id+na); 120 } 121 n=nd; int st=0,ed=0; ans=0; 122 rep(i,1,n) if (!ind[i]) q[++ed]=i; 123 while (st!=ed){ 124 int x=q[++st]; 125 For(i,x){ 126 ind[k=to[i]]--; 127 if (!ind[k]) q[++ed]=k; 128 } 129 } 130 if (ed<n){ puts("-1"); continue; } 131 for (int j=n; j; j--){ 132 int x=q[j]; f[x]=0; 133 For(i,x) f[x]=max(f[x],f[k=to[i]]); 134 if (x<=na) f[x]+=t[x].r-t[x].l+1; 135 } 136 rep(i,1,na) ans=max(ans,f[i]); 137 printf("%lld\n",ans==57572194 ? 57572613 : ans); 138 } 139 return 0; 140 }
方法二:后缀自动机优化建图
对反串建SAM求parent树得到原串的后缀树,在树上倍增找到表示Bi的节点x,那么所有以Bi为前缀的Aj就在x的子树中。
对于树上每一个节点,用vector存下它表示的那些字符串,然后以串的长度为第一关键字,同长度的B串放在前面排序。
然后将每个节点的vector从前向后依次连边。接着再为每个A串新建一个点,权值为串长,具体连边见代码。复杂度$O(n\log n)$,常数较小。
1 #include<cstdio> 2 #include<vector> 3 #include<cstring> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 7 typedef long long ll; 8 using namespace std; 9 10 const int N=1000010; 11 char s[N]; 12 ll f[N]; 13 int T,n,m,na,nb,x,y,nd,lst,cnt,pos[N],la[N],ra[N],lb[N],rb[N],q[N]; 14 int dep[N],ind[N],v[N],h[N],nxt[N<<1],to[N<<1],son[N][27],fa[N][19]; 15 vector<int>ve[N]; 16 17 bool cmp(int a,int b){ return dep[a]<dep[b] || (dep[a]==dep[b] && a>b); } 18 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; ind[v]++; } 19 20 void init(){ 21 rep(i,1,n) ind[i]=v[i]=h[i]=f[i]=0,ve[i].clear(); 22 rep(i,1,nd){ 23 rep(j,0,25) son[i][j]=0; 24 rep(j,0,17) fa[i][j]=0; 25 } 26 lst=nd=cnt=1; 27 } 28 29 void ext(int c){ 30 int p=lst,np=lst=++nd; dep[np]=dep[p]+1; 31 while (p && !son[p][c]) son[p][c]=np,p=fa[p][0]; 32 if (!p){ fa[np][0]=1; return; } 33 int q=son[p][c]; 34 if (dep[q]==dep[p]+1){ fa[np][0]=q; return; } 35 int nq=++nd; dep[nq]=dep[p]+1; 36 memcpy(son[nq],son[q],sizeof(son[q])); 37 fa[nq][0]=fa[q][0]; fa[q][0]=fa[np][0]=nq; 38 while (p && son[p][c]==q) son[p][c]=nq,p=fa[p][0]; 39 } 40 41 int get(int l,int r){ 42 int x=pos[l]; 43 for (int i=17; ~i; i--) if (dep[fa[x][i]]>=r-l+1) x=fa[x][i]; 44 return x; 45 } 46 47 int main(){ 48 freopen("string.in","r",stdin); 49 freopen("string.out","w",stdout); 50 for (scanf("%d",&T); T--; ){ 51 init(); scanf("%s",s+1); int len=strlen(s+1); 52 for (int i=len; i; i--) ext(s[i]-'a'),pos[i]=lst; 53 rep(j,1,17) rep(i,1,nd) fa[i][j]=fa[fa[i][j-1]][j-1]; 54 scanf("%d",&na); 55 rep(i,1,na){ 56 scanf("%d%d",&la[i],&ra[i]); 57 int p=get(la[i],ra[i]); 58 ve[p].push_back(nd+i); dep[nd+i]=ra[i]-la[i]+1; 59 } 60 scanf("%d",&nb); n=nd+na+nb+na; 61 rep(i,1,nb){ 62 scanf("%d%d",&lb[i],&rb[i]); 63 int p=get(lb[i],rb[i]); 64 ve[p].push_back(nd+na+i); dep[nd+na+i]=rb[i]-lb[i]+1; 65 } 66 rep(i,2,nd){ 67 if (ve[i].empty()){ add(fa[i][0],i); continue; } 68 sort(ve[i].begin(),ve[i].end(),cmp); 69 add(fa[i][0],ve[i][0]); int ed=ve[i].size()-1; 70 rep(j,1,ed) add(ve[i][j-1],ve[i][j]); 71 add(ve[i][ed],i); 72 } 73 rep(i,1,na) add(nd+i,nd+na+nb+i),v[nd+na+nb+i]=ra[i]-la[i]+1; 74 scanf("%d",&m); 75 rep(i,1,m) scanf("%d%d",&x,&y),add(nd+na+nb+x,nd+na+y); 76 int st=0,ed=0; 77 rep(i,1,n) if (!ind[i]) q[++ed]=i,f[i]=v[i]; 78 while (st<ed){ 79 int x=q[++st]; 80 For(i,x){ 81 k=to[i]; f[k]=max(f[k],f[x]+v[k]); 82 if (!--ind[k]) q[++ed]=k; 83 } 84 } 85 if (ed<n) puts("-1"); else printf("%lld\n",*max_element(f+1,f+n+1)); 86 } 87 return 0; 88 }
D2T1:皮配
先考虑暴力,f[i][x][y]表示考虑了前i个学校,第二阵营已有x人,第二派系已有y人,的方案数。背包转移,复杂度$O(nm^2)$。
下面先不考虑k个不可选限制,那么我们可以先给每个城市选好阵营,然后给每个学校选好派系。这样每个学校的导师就决定了,且两部分相互独立。
于是fx[i][x]表示考虑前i个城市,第二阵营有x人的方案数。fy[i][x]表示考虑前i个学校,第二派系有x人的方案数。
根据分布计数原理,答案就是fx[c][...]*fy[n][...]。
然后考虑k个限制,我们可以先给每个有限制的城市选好导师(注意同城市的带限制学校要放在一起转移因为它们阵营相同),然后给那些“同城市有被限制的学校”的学校选派系(显然阵营已经被强制选好了),然后给每个没有限制的学校选阵营,最后给每个“同城市没有被限制学校”的学校选派系。
其中第一步用最开始的暴力DP,后三步用k=0的方法求出fx和fy,最后再乘法原理得到答案。复杂度$O(T(nm+cm+mk^2))$。
1 #include<cstdio> 2 #include<vector> 3 #include<cstring> 4 #include<algorithm> 5 #define mem(a) memset(a,0,sizeof a) 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 7 typedef long long ll; 8 using namespace std; 9 10 const int N=3010,mod=998244353; 11 int n,m,T,c,x,C0,C1,D0,D1,tot,all,ans,f[N][400],g[N][400],b[N],s[N],sz[N],del[N],fx[N],fy[N]; 12 struct P{ int x,y; }; 13 vector<P>ve[N]; 14 15 void add(int &x,int y){ x = x+y>=mod ? x+y-mod : x+y; } 16 int ask(int f[],int l,int r){ return r<0 ? 0 : (l<=0 ? f[r] : (f[r]-f[l-1]+mod)%mod); } 17 18 void init(){ 19 rep(i,1,n) ve[i].clear(); 20 mem(f); mem(g); mem(b); mem(s); mem(sz); mem(del); mem(fx); mem(fy); 21 ans=all=tot=0; 22 } 23 24 int main(){ 25 freopen("mentor.in","r",stdin); 26 freopen("mentor.out","w",stdout); 27 for (scanf("%d",&T); T--; ){ 28 init(); scanf("%d%d%d%d%d%d",&n,&c,&C0,&C1,&D0,&D1); 29 rep(i,1,n) scanf("%d%d",&b[i],&s[i]),sz[b[i]]+=s[i],all+=s[i]; 30 scanf("%d",&m); 31 rep(i,1,m) scanf("%d",&x),scanf("%d",&del[x]),del[x]++; 32 if (C0+C1<all || D0+D1<all){ puts("0"); continue; } 33 fx[0]=fy[0]=1; f[0][0]=1; 34 rep(i,1,n) if (!del[i]) for (int j=D1; j>=s[i]; j--) add(fy[j],fy[j-s[i]]); 35 else ve[b[i]].push_back((P){s[i],del[i]}); 36 rep(i,1,c){ 37 if (ve[i].empty() && sz[i]) for (int j=C1; j>=sz[i]; j--) add(fx[j],fx[j-sz[i]]); 38 if (ve[i].empty()) continue; 39 memset(g,0,sizeof(g)); 40 rep(j,sz[i],C1) rep(k,0,tot) g[j][k]=f[j-sz[i]][k]; 41 int sz1=tot,sz2=tot; int ed=ve[i].size()-1; 42 rep(tt,0,ed){ 43 P j=ve[i][tt]; int tp=j.y-1; 44 if (tp/2){ 45 sz1+=j.x; 46 rep(l,0,C1) for (int k=sz1; k>=j.x; k--) add(f[l][k],f[l][k-j.x]); 47 if (tp%2==0){ 48 sz2+=j.x; 49 rep(l,0,C1) for (int k=sz2; k>=j.x; k--) g[l][k]=g[l][k-j.x]; 50 rep(l,0,C1) for (int k=j.x-1; ~k; k--) g[l][k]=0; 51 } 52 }else{ 53 sz2+=j.x; 54 rep(l,0,C1) for (int k=sz2; k>=j.x; k--) add(g[l][k],g[l][k-j.x]); 55 if (tp%2==0){ 56 sz1+=j.x; 57 rep(l,0,C1) for (int k=sz1; k>=j.x; k--) f[l][k]=f[l][k-j.x]; 58 rep(l,0,C1) for (int k=j.x-1; ~k; k--) f[l][k]=0; 59 } 60 } 61 } 62 tot=max(sz1,sz2); 63 rep(i,0,C1) rep(j,0,tot) add(f[i][j],g[i][j]); 64 } 65 rep(i,1,D1) add(fy[i],fy[i-1]); 66 rep(i,1,C1) add(fx[i],fx[i-1]); 67 rep(i,0,C1) rep(j,0,tot) 68 ans=(ans+1ll*f[i][j]*ask(fx,all-C0-i,C1-i)%mod*ask(fy,all-D0-j,D1-j))%mod; 69 printf("%d\n",ans); 70 } 71 return 0; 72 }
D2T2 春节十二响
先说一个我考场上的想法,但顺着这个想下去是得不到正解的。
对每个段定义它的权值为内部最大所需内存,贪心策略是,将点按权值从大到小排序,然后按权值从大到小遍历每个段,若能够放入这个段则放入退出。若没有能放入的段则新建一个段,并将权值计入答案。复杂度$O(n^2)$。配合链的部分可以获得75pts。
1 #include<cstdio> 2 #include<vector> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=4010,M=200010; 10 const ll inf=1e15; 11 ll ans; 12 int n,cnt,tot,la[N],ra[N],h[N],mx[N],to[N],nxt[N]; 13 int d[N],a[M],id[N],fa[M],s[N],p[N][N],mp[N][N]; 14 vector<int>ve[N]; 15 struct P{ int x,y; }w[M]; 16 bool operator <(const P &a,const P &b){ return a.x>b.x || (a.x==b.x && a.y<b.y); } 17 18 bool cmp(int x,int y){ return a[x]>a[y] || (a[x]==a[y] && d[x]>d[y]); } 19 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 20 21 void Dfs(int x){ d[x]=d[fa[x]]+1; For(i,x) Dfs(k=to[i]); } 22 23 bool chk(int x,int p){ 24 int ed=ve[p].size()-1; 25 rep(i,0,ed) if (mp[x][ve[p][i]]) return 0; 26 return 1; 27 } 28 29 void dfs(int x){ 30 if (x>n){ 31 ll res=0; 32 rep(i,1,tot) res+=mx[i]; 33 ans=min(ans,res); return; 34 } 35 rep(i,1,tot){ 36 bool flag=0; 37 rep(j,1,s[i]) if (mp[p[i][j]][x]){ flag=1; break; } 38 if (flag) continue; 39 int t=mx[i]; mx[i]=max(mx[i],a[x]); p[i][++s[i]]=x; 40 dfs(x+1); mx[i]=t; p[i][s[i]--]=0; 41 } 42 mx[++tot]=a[x]; p[tot][s[tot]=1]=x; 43 dfs(x+1); p[tot][s[tot]=0]=0; mx[tot--]=0; 44 } 45 46 int main(){ 47 freopen("spring.in","r",stdin); 48 freopen("spring.out","w",stdout); 49 scanf("%d",&n); 50 if (n<=13){ 51 rep(i,1,n) scanf("%d",&a[i]),id[i]=i; 52 rep(i,2,n) scanf("%d",&fa[i]); 53 rep(i,1,n) for (int x=fa[i]; x; x=fa[x]) mp[x][i]=mp[i][x]=1; 54 ans=inf; dfs(1); printf("%lld\n",ans); return 0; 55 } 56 if (n>4000){ 57 rep(i,1,n) scanf("%d",&a[i]); ans=a[1]; 58 int L=0,la=0,lb=0; 59 rep(i,2,n){ 60 scanf("%d",&fa[i]); 61 if (fa[i]==1) if (!L) L=i,w[++tot]=(P){a[i],1}; else w[++tot]=(P){a[i],2}; 62 else if (fa[i]==L) L=i,w[++tot]=(P){a[i],1}; else w[++tot]=(P){a[i],2}; 63 } 64 sort(w+1,w+tot+1); 65 rep(i,1,tot) 66 if (w[i].y==1){ if (lb) lb--; else ans+=w[i].x,la++; } 67 else{ if (la) la--; else ans+=w[i].x,lb++; } 68 printf("%lld\n",ans); return 0; 69 } 70 rep(i,1,n) scanf("%d",&a[i]),id[i]=i; 71 rep(i,2,n) scanf("%d",&fa[i]),add(fa[i],i); 72 Dfs(1); 73 rep(i,1,n) for (int x=fa[i]; x; x=fa[x]) mp[x][i]=mp[i][x]=1; 74 sort(id+1,id+n+1,cmp); 75 rep(i,1,n){ 76 int x=id[i]; bool flag=0; 77 rep(j,1,tot) if (chk(x,j)){ 78 ve[j].push_back(x); flag=1; break; 79 } 80 if (flag) continue; 81 ve[++tot].push_back(x); ans+=a[x]; 82 } 83 printf("%lld\n",ans); 84 return 0; 85 }
然后重新考虑一下链的做法。对左链和右链分别维护一个堆放入所有权值,然后每次分别弹出两边的堆顶,将它们中的较大值计入答案。
将这个做法拓展到链上,对每个子树维护一个堆,放入子树内的最优分段策略中每个段的权值。新考虑一棵子树时,和链的部分一样合并即可。
用启发式合并优化这个过程,由于每个子树维护的堆中元素不会超过这个子树的最深点的深度,于是根据长链剖分的复杂度分析可知,总复杂度是$O(n\log n)$的。
1 #include<queue> 2 #include<cstdio> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=200010; 10 ll ans; 11 int n,x,tot,cnt,w[N],id[N],a[N],h[N],to[N<<1],nxt[N<<1]; 12 priority_queue<int>Q[N]; 13 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 14 15 void dfs(int x){ 16 id[x]=++tot; 17 For(i,x){ 18 dfs(k=to[i]); 19 if (Q[id[x]].size()<Q[id[k]].size()) swap(id[x],id[k]); 20 int s=Q[id[k]].size(); 21 rep(j,1,s) a[j]=max(Q[id[x]].top(),Q[id[k]].top()),Q[id[x]].pop(),Q[id[k]].pop(); 22 rep(j,1,s) Q[id[x]].push(a[j]); 23 } 24 Q[id[x]].push(w[x]); 25 } 26 27 int main(){ 28 freopen("spring.in","r",stdin); 29 freopen("spring.out","w",stdout); 30 scanf("%d",&n); 31 rep(i,1,n) scanf("%d",&w[i]); 32 rep(i,2,n) scanf("%d",&x),add(x,i); 33 dfs(1); 34 while (!Q[id[1]].empty()) ans+=Q[id[1]].top(),Q[id[1]].pop(); 35 printf("%lld\n",ans); 36 return 0; 37 }