十二省联考2019部分题解

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

方法一:类似[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 }
View Code

方法二:

注意到对于同一个左端点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 }
View Code

 

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

方法一:后缀数组+主席树优化建图

先只考虑所有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 }
View Code

方法二:后缀自动机优化建图

对反串建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 }
View Code

 

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

 

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

然后重新考虑一下链的做法。对左链和右链分别维护一个堆放入所有权值,然后每次分别弹出两边的堆顶,将它们中的较大值计入答案。

将这个做法拓展到链上,对每个子树维护一个堆,放入子树内的最优分段策略中每个段的权值。新考虑一棵子树时,和链的部分一样合并即可。

用启发式合并优化这个过程,由于每个子树维护的堆中元素不会超过这个子树的最深点的深度,于是根据长链剖分的复杂度分析可知,总复杂度是$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 }
View Code

 

posted @ 2019-04-14 11:22  HocRiser  阅读(236)  评论(0编辑  收藏  举报