2019 3.27~4.4

还有不几天就省选了,一定要稳住心态,倍加努力

 

 

 

2019 3 27 T1 spy (打表)

题目大意:已知$b_{i}=\sum\limits_{i=0}^{n-1} f((i\;or\;j)\;xor\;i)a_{i}$,现在给出$b$数组,让你还原$a$数组。$n\leq 2^{20}$

打表题..

先考虑暴力怎么写,似乎构造出矩阵F再高消一下就行了,时间复杂度$O(n^{3})$

观察这个矩阵,我们把它分4块,发现它竟然满足这样神奇的性质

左上,左下,右下是同构的,右上则是取反

设$F^{-1}$是$F$的逆矩阵,则$A=F^{-1}B$

观察这个矩阵的性质,发现把最右上角的一个数改成1,得到$G$矩阵

发现左上,右上,右下是同构的,左下则是*-1之后的矩阵,且把这个逆矩阵的每一项乘$2^{n-1}$后,矩阵中只有1和-1

推一下式子

这是什么?蝴蝶操作!我们不断递归下去,最后只剩下一个1*1的矩阵,里面有一个1。

那么我们递归求解一下就行了,别忘了把系数乘回来,再处理一下右上角的贡献

什么?爆long long了,发现蝴蝶操作其实是可逆的,这样就不会用减法减去一个特别大的数了

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #define N1 1048599
 6 #define ll long long 
 7 #define ull unsigned long long 
 8 #define dd double
 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 
19 int n,L;
20 int a[N1];
21 ll b[N1],c[N1];
22 
23 int main()
24 {
25     freopen("spy.in","r",stdin);
26     freopen("spy.out","w",stdout);
27     scanf("%d",&n);
28     int i,j,k,cnt; ll tmp;
29     for(i=0;i<n;i++) read(b[i]);
30     memcpy(c,b,sizeof(c));
31     //for(k=n;k>=2;k>>=1)
32     for(k=2;k<=n;k<<=1)
33     {
34         for(i=0;i<n;i+=k)
35         for(j=0;j<(k>>1);j++)
36         {
37             tmp=c[i+j+(k>>1)];
38             c[i+j+(k>>1)]-=c[i+j];
39             c[i+j]+=tmp;
40         }
41     }
42     for(L=0;(1<<L)<n;L++);
43     for(i=0;i<n;i++) c[i]>>=L-1;
44     cnt=(n>>1)-1; c[0]=0;
45     for(i=0;i<n-1;i++){ 
46         c[0]+=b[i]; while(c[0]>0&&cnt>0) c[0]-=b[n-1], cnt--; }
47     c[0]>>=L-1;
48     for(i=0;i<n;i++) printf("%lld ",c[i]);
49     return 0;
50 }
View Code

 

 

 

2019 3 27 T3 network (容斥+带撤销的并查集)

题目大意:给你一棵$n$个节点的树,有$Q$次修改边权的操作,求每次操作后图中边权$gcd$为1的路径数,$n\leq 10^{5},ei\leq 10^{6},Q\leq 100$

$gcd$为1?考虑利用$\mu$函数容斥的套路,$ans=\sum f(i)\mu(i)$

考虑把询问离线

我们暴力枚举$\mu$不是0的$i$,依次处理修改边权对$f(i)$的影响,然后把$f(i)$的贡献加到这次修改的答案上

对于$n$个节点的树而言,它对能产生的路径数是$n(n-1)/2$

那么现在问题变成,给你一个森林,要我们资瓷断边和连边,同时询问每个连通块内点的个数

并查集?

发现修改次数很少,所以我们先把$i$状态中不会被修改的边路径压缩连在一起,然后把可能会被修改的边按秩合并连在一起,这样就能在能够撤销修改的同时减小常数啦

修改可能是断边,也可能是连边

对于断边而言,假设要被修改的边是e 我们需要把时间戳回溯到连上$e$之前,回溯过程需要把在$e$之后连上的边全都断开,把$e$断开,再重新把e之后断掉的那些边连上,才能保证我们在并查集里只删除了$e$这一条边

对于连边而言,直接连上就行了吧..别忘了修改边对应的时间戳

这个过程时间复杂度是$O(Q^{2}logn)$的

设$10^{6}$范围内$\mu$不为0的数有$K$个,平均因子数是$S$个,那么总时间复杂度是$O(Kn+S(n+Q^{2})logn)$

代码实现极为丧病

  1 #include <vector>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define N1 100050
  6 #define O1 105
  7 #define ull unsigned long long 
  8 #define ll long long
  9 using namespace std;
 10 const int maxn=1000000;
 11 #define M1 maxn+10
 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 
 21 struct Edge{ 
 22 int x[N1],y[N1],w[N1],cte;
 23 void ae(int X,int Y,int W)
 24 { cte++; x[cte]=X; y[cte]=Y; w[cte]=W; }
 25 }e;
 26 
 27 int gcd(int x,int y)
 28 {
 29     if(!y) return x;
 30     return gcd(y,x%y);
 31 } 
 32 
 33 namespace theory{
 34 int fac[N1],num,pfac[N1],d[N1],pnum;
 35 int pr[M1],use[M1],mu[M1],nxt[M1],cnt;
 36 void get_pr()
 37 {
 38     int i,j; mu[1]=1;
 39     for(i=2;i<=maxn;i++)
 40     {
 41         if(!use[i]){ pr[++cnt]=i; mu[i]=-1; nxt[i]=i; }
 42         for(j=1;j<=cnt&&i*pr[j]<=maxn;j++)
 43         {
 44             use[i*pr[j]]=1; nxt[i*pr[j]]=pr[j];
 45             if(i%pr[j]){ mu[i*pr[j]]=-mu[i]; }
 46             else{ mu[i*pr[j]]=0; break; }
 47         }
 48     }
 49 }
 50 void dfs_fac(int s,int dep)
 51 {
 52     if(dep==pnum+1){ fac[++num]=s; return; }
 53     for(int i=0;i<=d[dep];i++)
 54     {
 55         dfs_fac(s,dep+1), s*=pfac[dep];
 56     }
 57 }
 58 void get_fac(int x)
 59 {
 60     int i,p; num=pnum=0;
 61     while(x!=1)
 62     {
 63         pfac[++pnum]=nxt[x], d[pnum]=0, p=nxt[x];
 64         while(x%p==0) d[pnum]++, x/=p;
 65     }
 66     dfs_fac(1,1);
 67 }
 68 };
 69 
 70 using theory::fac; using theory::num; using theory::mu;
 71 
 72 struct Pair{ 
 73 int x,y; 
 74 Pair(int x,int y):x(x),y(y){}
 75 Pair(){}
 76 };
 77 struct node{
 78 int fx,fy,rx,ry,id;
 79 node(int fx,int fy,int rx,int ry,int id):fx(fx),fy(fy),rx(rx),ry(ry),id(id){}
 80 node(){}
 81 };
 82 
 83 int n,de;
 84 int opid[N1],opw[N1],mixable[N1],Q,tim[N1]; ll ANS[O1]; bool vis[110];
 85 vector<int>org[M1]; 
 86 vector<Pair>op[M1];
 87 node mix[O1];
 88 
 89 int fa[N1],h[N1],sz[N1];
 90 int findfa0(int x){ while(x!=fa[x]) x=fa[x]; return x; }
 91 int findfa1(int x)
 92 {
 93     int y=x,pre;
 94     while(y!=fa[y]) y=fa[y];
 95     while(fa[x]!=y) pre=fa[x], fa[x]=y, x=pre;
 96     return y;
 97 }
 98 inline ll C(int x){ return 1ll*x*(x-1)/2; }
 99 
100 void cut(node K,ll &sum)
101 {
102     int x=K.fx, y=K.fy, fx=findfa0(x);
103     sum-=C(sz[fx]); 
104     while(x!=fx) sz[x]-=sz[y], x=fa[x]; sz[x]-=sz[y]; fa[y]=y; 
105     sum+=C(sz[fx])+C(sz[y]); 
106 }
107 
108 void merge(node &K,ll &sum)
109 {
110     int x=K.rx, y=K.ry;
111     x=findfa0(x); y=findfa0(y);
112     if(sz[y]>sz[x]) swap(x,y);
113     K.fx=x, K.fy=y;
114     sum-=C(sz[x])+C(sz[y]); 
115     fa[y]=x; sz[x]+=sz[y];
116     sum+=C(sz[x]); 
117 }
118 
119 void calc(int w)
120 {
121     int i,j,x,y,id,fx,fy,tot=0,t,la=0; ll sum=0,pre; Pair K;
122     for(i=0;i<org[w].size();i++)
123     {
124         x=e.x[org[w][i]]; y=e.y[org[w][i]]; 
125         sz[x]=sz[y]=1; fa[x]=x; fa[y]=y;
126     }
127     for(i=0;i<op[w].size();i++)
128     {
129         K=op[w][i]; x=e.x[opid[K.y]]; y=e.y[opid[K.y]];
130         sz[x]=sz[y]=1; fa[x]=x; fa[y]=y; mixable[opid[K.y]]=1;
131     }
132     for(i=0;i<org[w].size();i++)
133     {
134         id=org[w][i]; x=e.x[id]; y=e.y[id]; 
135         if(mixable[id]) continue;
136         x=findfa1(x); y=findfa1(y);
137         sum-=C(sz[x])+C(sz[y]);
138         fa[y]=x; sz[x]+=sz[y];
139         sum+=C(sz[x]);
140     }
141     for(i=0;i<org[w].size();i++)
142     {
143         id=org[w][i]; x=e.x[id]; y=e.y[id]; 
144         if(!mixable[id]) continue; 
145         mix[++tot]=(node){0,0,x,y,id}; tim[id]=tot;
146         merge(mix[tot],sum);
147     }
148     ANS[0]+=mu[w]*sum; pre=sum;
149     /*if(w==2)
150         de=1;*/
151     memset(vis,0,sizeof(vis));
152     for(i=0;i<op[w].size();i++)
153     {
154         K=op[w][i]; id=opid[K.y];
155         for(j=la+1;j<=K.y-1;j++) ANS[j]+=mu[w]*sum; la=K.y;
156         if(!K.x){
157         
158         for(j=tot;j>=tim[id];j--) 
159         {
160             cut(mix[j],sum); 
161         }
162         for(j=tim[id];j<tot;j++)
163         {
164             mix[j]=mix[j+1]; tim[mix[j].id]=j;
165             merge(mix[j],sum);
166         }
167         tot--;
168         
169         }else{
170         
171         x=e.x[id]; y=e.y[id];
172         mix[++tot]=(node){0,0,x,y,id}; tim[id]=tot;
173         merge(mix[tot],sum);
174         
175         }
176         if((i+1==op[w].size()) || (op[w][i].y!=op[w][i+1].y)) ANS[K.y]+=sum*mu[w]; 
177     }
178     for(j=la+1;j<=Q;j++) ANS[j]+=mu[w]*sum; 
179     for(i=0;i<op[w].size();i++)
180     {
181         K=op[w][i];
182         mixable[opid[K.y]]=0;
183     }
184 }
185 
186 int noww[N1];
187 
188 int main()
189 {
190     freopen("network.in","r",stdin);
191     freopen("network.out","w",stdout);
192     int i,j,k,x,y,w,q,tot=0;
193     scanf("%d",&n); 
194     for(i=1;i<n;i++) read(x), read(y), read(w), e.ae(x,y,w); //e.ae(x,y,z), e.ae(y,x,z);
195     theory::get_pr();
196     for(i=1;i<n;i++) 
197     {
198         theory::get_fac(e.w[i]); noww[i]=e.w[i];
199         for(j=1;j<=num;j++) if(mu[fac[j]])
200             org[fac[j]].push_back(i);
201     }
202     read(Q);
203     for(q=1;q<=Q;q++)
204     {
205         read(opid[q]); read(opw[q]);
206         theory::get_fac(noww[opid[q]]); 
207         for(j=1;j<=num;j++) op[fac[j]].push_back(Pair(0,q)); 
208         noww[opid[q]]=opw[q];
209         theory::get_fac(noww[opid[q]]); 
210         for(j=1;j<=num;j++) op[fac[j]].push_back(Pair(1,q)); 
211     }
212     for(i=1;i<=n;i++) fa[i]=i;
213     for(i=1;i<=maxn;i++) if(mu[i])
214     {
215         calc(i);
216     }
217     for(q=0;q<=Q;q++) printf("%lld\r\n",ANS[q]);
218     return 0;
219 }
View Code

 

 

 

2019 3 29 T2 原样输出 (后缀自动机)

题目大意:给你$n$个字符串,要求你在每个字符串取出一个子串并把它们按顺序连接在一起,形成一个新的串。求不相同的新的串的数量,一些测试点需要按字典序输出这些新串,$\sum len \leq 2^{20}$

后缀自动机好题

众所周知,$SAM$有如下性质:从根节点开始沿着$trs$图走,能走不重不漏地走出这个字符串所有的子串

考虑$n=1$的情况,我们直接对这个串建出$SAM$,并在$trs$图上拓扑即可,输出方案就在$trs$图上贪心$dfs$

如果$n$更大呢?

广义$SAM$并不能很好地解决这个问题..

考虑建出$n$个$SAM$,然后想办法把它们连在一个图里,再用之前的方法求答案,也就是补全trs指针啦

补全$trs$指针的方法和$trie$图类似..

比如当$!trs[x][c]$时,我们要找出$x$所在串$S$后面的串中,第一个出现字符$c$的串$T$,然后把$x$和$c$在$T$的自动机中对应的节点连起来

可以用个队列实现

注意$c$必须是根节点直接连接到的$endpos$节点,不能是辅助节点。因为辅助节点并不是根节点直接到达的,会违背$SAM$的性质

注意输出字符串要用putchar而不是printf,不然会T得很惨

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #define ll long long
  5 #define N1 1100000
  6 #define M1 N1*3
  7 using namespace std;
  8 const int p=1000000007;
  9 
 10 template <typename _T> void read(_T &ret)
 11 {
 12     ret=0; _T fh=1; char c=getchar(); 
 13     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
 14     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
 15     ret=ret*fh;
 16 }
 17 
 18 inline int idx(char c)
 19 {
 20     if(c=='A') return 0;
 21     if(c=='C') return 1;
 22     if(c=='G') return 2;
 23     if(c=='T') return 3;
 24 }
 25 inline char _idx(int x)
 26 {
 27     if(x==0) return 'A';
 28     if(x==1) return 'C';
 29     if(x==2) return 'G';
 30     if(x==3) return 'T';
 31 }
 32 
 33 int n;
 34 char str[N1];
 35 int a[N1],id[N1],Len,to[N1][4],stk[N1],tp; 
 36 
 37 namespace SAM{
 38 int trs[M1][4],pre[M1],dep[M1],root[N1],pos[N1],la,tot;
 39 void init(){ la=tot=1; }
 40 int Newroot(){ la=++tot; return tot; }
 41 int insert(int c,int rt)
 42 {
 43     int p=la,q,np=++tot,nq;
 44     dep[np]=dep[p]+1; la=np;
 45     for(;p&&!trs[p][c];p=pre[p]) trs[p][c]=np;
 46     if(!p){ pre[np]=rt; return np; }
 47     q=trs[p][c];
 48     if(dep[q]==dep[p]+1) pre[np]=q;
 49     else{
 50         pre[nq=++tot]=pre[q];
 51         pre[np]=pre[q]=nq;
 52         dep[nq]=dep[p]+1;
 53         memcpy(trs[nq],trs[q],sizeof(trs[nq]));
 54         for(;p&&trs[p][c]==q;p=pre[p]) trs[p][c]=nq;
 55     }
 56     return np;
 57 }
 58 
 59 int inc[M1],que[M1],hd,tl; ll sum[M1],ANS;
 60 char now[N1];
 61 void print(int dep)
 62 {
 63     for(int i=1;i<dep;i++) putchar(now[i]); //printf("%c",now[i]);
 64     ANS++; puts("");
 65 }
 66 void dfs_ans(int x,int dep)
 67 {
 68     print(dep);
 69     for(int i=0;i<4;i++)
 70     {
 71         if(trs[x][i]) 
 72         {
 73             now[dep]=_idx(i);
 74             dfs_ans(trs[x][i],dep+1);
 75         }
 76     }
 77 }
 78 void topo()
 79 {
 80     int i,c,x;
 81     hd=1,tl=0; sum[1]=1;
 82     for(i=1;i<=n;i++) que[++tl]=root[i];
 83     while(hd<=tl)
 84     {
 85         x=que[hd++]; (ANS+=sum[x])%=p;
 86         for(c=0;c<4;c++) if(trs[x][c])
 87         {
 88             (sum[trs[x][c]]+=sum[x])%=p;
 89             inc[trs[x][c]]--;
 90             if(!inc[trs[x][c]]) que[++tl]=trs[x][c];
 91         }
 92     }
 93 }
 94 void connect()
 95 {
 96     int i,c,x,j; pos[0]=1;
 97     for(i=0;i<=Len;i++)
 98     {
 99         x=pos[i];
100         for(c=0;c<4;c++) if(to[i][c] && !trs[x][c] && id[i]!=id[to[i][c]]) trs[x][c]=pos[to[i][c]];
101     }
102     for(i=1;i<=tot;i++)
103     for(c=0;c<4;c++) if(trs[i][c])
104         inc[trs[i][c]]++;
105 }
106 void get_to(int c)
107 {
108     int i,j; 
109     hd=1,tl=0; que[++tl]=0;
110     for(i=1;i<n;i++)
111     {
112         if(trs[root[i]][c])
113             while(hd<=tl) trs[que[hd]][c]=trs[root[i]][c], hd++;
114         for(j=root[i];j<=root[i+1];j++)
115         {
116             if(!trs[j][c]) que[++tl]=j;
117         }
118     }
119     if(trs[root[n]][c])
120         while(hd<=tl) trs[que[hd]][c]=trs[root[n]][c], hd++;
121 }
122 
123 };
124 
125 
126 
127 int main()
128 {
129     freopen("copy.in","r",stdin);
130     freopen("copy.out","w",stdout);
131     int i,j,len,c,K;
132     scanf("%d",&n);
133     for(i=1;i<=n;i++)
134     {
135         SAM::root[i]=SAM::Newroot(); 
136         scanf("%s",str+1); len=strlen(str+1);
137         for(j=1;j<=len;j++)
138         { 
139             c=idx(str[j]); a[++Len]=c; id[Len]=i;
140             SAM::pos[Len]=SAM::insert(c,SAM::root[i]); 
141         }
142     }
143     for(c=0;c<4;c++) SAM::get_to(c);
144     scanf("%d",&K);
145     SAM::connect();
146     if(K==0) SAM::topo();
147     else SAM::dfs_ans(1,1);
148     printf("%lld\n",SAM::ANS);
149     return 0;
150 }
View Code

 

 

 

2019 3 29 T3 不同的缩写 (Trie树+贪心+最大流)

题目大意:给你$n$个串,对于每个串,让你找出一个子序列,最终使得任意两个串找出的子序列都不相同,要求最长的子序列最短,同时输出方案,$n,len\leq 300$

神仙般的模型转化题

用到了一个以前用过的套路然而我考场上还是忘了。对于每个串只需要找出n个子序列就能避免冲突了

最长子序列最短?二分子序列长度上界

找子序列的过程可以用stl的string类+hash+map判重实现

然后建图跑最大流就行了吧

这里又用到了一个小技巧。可以建出$trie$树来表示字符串,判重的同时还得到了代表一个字符串的点的标号

时间O(被卡常)

  1 #include <map>
  2 #include <string>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <iostream>
  6 #include <algorithm>
  7 #define ll long long
  8 #define N1 305 
  9 #define M1 N1*N1*2
 10 #define ull unsigned long long 
 11 using namespace std;
 12 const int seed=131;
 13 const int inf=0x3f3f3f3f;
 14 
 15 template <typename _T> void read(_T &ret)
 16 {
 17     ret=0; _T fh=1; char c=getchar(); 
 18     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
 19     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
 20     ret=ret*fh;
 21 }
 22 
 23 int idx(char c){ return c-'a'+1; }
 24 char _idx(int x){ return x+'a'-1; }
 25 
 26 struct Edge{
 27 int to[M1*3],nxt[M1*3],flow[M1*3],head[M1],cte;
 28 void ae(int u,int v,int f)
 29 { cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte; flow[cte]=f; }
 30 void clr()
 31 { memset(to,0,(cte+1)*4); memset(nxt,0,(cte+1)*4); memset(flow,0,(cte+1)*4); memset(head,0,sizeof(head)); cte=0; }
 32 }e;
 33 
 34 struct Trie{
 35 int ch[M1][27],val[M1],fa[M1],tot;
 36 void clr(){ memset(ch,0,(tot+1)*27*4); memset(val,0,(tot+1)*4); memset(fa,0,(tot+1)*4); tot=0; }
 37 int ins(string &s)
 38 {
 39     int i,x=0,c;
 40     for(i=0;i<s.size();i++)
 41     {
 42         c=idx(s[i]);
 43         if(!ch[x][c]) ch[x][c]=++tot, val[tot]=c, fa[tot]=x;
 44         x=ch[x][c];
 45     }
 46     return x;
 47 }
 48 int stk[N1],tp;
 49 void copy(string &s,int x)
 50 {
 51     s.clear();
 52     for(tp=0;x;x=fa[x]) stk[++tp]=x;
 53     while(tp) s+=_idx(val[stk[tp--]]);
 54 }
 55 }tr;
 56 
 57 int n,de;
 58 
 59 namespace Dinic{
 60 
 61 int S,T,N,hd,tl;
 62 int dep[M1],cur[M1],que[M1];
 63 
 64 int bfs()
 65 {
 66     int x,j,v;
 67     memset(dep,-1,sizeof(dep)); memcpy(cur,e.head,sizeof(cur));
 68     hd=1,tl=0; que[++tl]=S; dep[S]=0;
 69     while(hd<=tl)
 70     {
 71         x=que[hd++];
 72         for(j=e.head[x];j;j=e.nxt[j])
 73         {
 74             v=e.to[j]; 
 75             if(dep[v]==-1 && e.flow[j]>0) 
 76                 dep[v]=dep[x]+1, que[++tl]=v; 
 77         }
 78     }
 79     return dep[T]!=-1;
 80 }
 81 int dfs(int x,int limit)
 82 {
 83     if(x==T||!limit) return limit;
 84     int j,v,flow,ans=0;
 85     for(j=cur[x];j;j=e.nxt[j])
 86     {
 87         v=e.to[j]; cur[x]=j;
 88         if((dep[v]==dep[x]+1) && (flow=dfs(v,min(limit,e.flow[j]))))
 89         {
 90             e.flow[j]-=flow; limit-=flow;
 91             e.flow[j^1]+=flow; ans+=flow;
 92             if(!limit) break;
 93         }
 94     }
 95     return ans;
 96 }
 97 int Main()
 98 {
 99     int mxflow=0; 
100     while(bfs()) mxflow+=dfs(S,inf);
101     return mxflow;
102 }
103 
104 };
105 
106 string str[N1],ANS[N1];
107 
108 map<ull,int>mp;
109 
110 struct SubStr{
111 string s[N1]; ull hsh[N1]; int cnt,pos[N1]; 
112 void clr()
113 {
114     for(int i=0;i<cnt;i++) s[i].clear();
115     memset(hsh,0,cnt*8); memset(pos,0,cnt*4);
116     cnt=0;
117 }
118 void get(int id,int limit)
119 {
120     int i,j,now; ull tmp; char c; mp.clear(); clr();
121     for(i=0;i<str[id].size();i++)
122     {
123         now=cnt; c=str[id][i];
124         
125         tmp=idx(c);
126         if(mp.find(tmp)==mp.end()) 
127         {
128             mp[tmp]=1;
129             s[cnt]=c; hsh[cnt]=tmp; cnt++; 
130             if(cnt==n) break;     
131         }
132         
133         for(j=0;j<now;j++)
134         {
135             if(s[j].size()==limit) continue;
136             tmp=hsh[j]*seed+idx(c);
137             if(mp.find(tmp)!=mp.end()) continue; mp[tmp]=1;
138             s[cnt]=s[j]+c; hsh[cnt]=tmp; cnt++; 
139             if(cnt==n) break;
140         }
141         if(cnt==n) break; 
142     }
143 }
144 void ins_trie(int id)
145 {
146     int i;
147     for(i=0;i<cnt;i++) pos[i]=tr.ins(s[i]);
148     for(i=0;i<cnt;i++) e.ae(id,n+pos[i],1), e.ae(n+pos[i],id,0);
149 }
150 }F[N1];
151 
152 
153 int check(int limit)
154 {
155     int i,j,x,v,ans;
156     for(i=1;i<=n;i++) F[i].get(i,limit);
157     tr.clr(); e.clr(); e.cte=1;
158     for(i=1;i<=n;i++) F[i].ins_trie(i);
159     for(i=1;i<=n;i++) e.ae(0,i,1), e.ae(i,0,0);
160     Dinic::T=n+tr.tot+1;
161     for(i=1;i<=tr.tot;i++) e.ae(n+i,Dinic::T,1), e.ae(Dinic::T,n+i,0);
162     ans=Dinic::Main();
163     if(ans<n) return 0;
164     for(x=1;x<=n;x++)
165     {
166         for(j=e.head[x];j;j=e.nxt[j])
167         {
168             v=e.to[j]; 
169             if(v>n && e.flow[j]==0) 
170                 tr.copy(ANS[x],v-n);
171         }
172     }
173     return 1;
174 }
175 
176 int main()
177 {
178     freopen("diff.in","r",stdin);
179     freopen("diff.out","w",stdout);
180     int i,j,l,r,mid,ans=-1;
181     scanf("%d",&n);
182     for(i=1;i<=n;i++) cin >> str[i];
183     l=1,r=n; 
184     while(l<=r)
185     {
186         mid=(l+r)>>1;
187         if(check(mid)) ans=mid,r=mid-1;
188         else l=mid+1;
189     }
190     if(ans==-1){ puts("-1"); return 0; }
191     printf("%d\n",ans);
192     for(i=1;i<=n;i++) cout<< ANS[i] << endl;
193     return 0;
194 }
View Code

 

 

 

2019 3 30 T1 啊 (博弈+贪心)

题目大意:小绿和小蓝小红和小蓝van游戏,首先给出一棵$n$个点的树,这棵树保证非叶子结点的儿子数量是奇数。他们两人依次在这棵树上还没被填上数字的叶子结点上填数,小红填1,小蓝填-1,直到把叶子结点都填满。可能有一些叶节点一开始就已经被填上了数。非叶子节点的数字=它相邻的儿子中较多的数字。根节点上是数是1则小红赢,反之小蓝赢。问最后谁能赢。如果小红能赢,输出小红第一步可以填数的叶子结点编号。$n\leq 10^{5}$

好一道思维题,输出方案的策略简直神仙。直接把我搞si了

因为每个节点只有奇数个儿子,所以输赢只有3种情况:红必胜,蓝必胜,先手胜

红必胜要满足 红必胜子树数量 > 蓝必胜子树数量

蓝必胜要满足 红必胜子树数量 < 蓝必胜子树数量

先手胜要满足 红必胜子树数量 = 蓝必胜子树数量

DP一下就能判断出谁能赢了

然而本题最duliu的地方在于输出方案

如果根节点是红必胜,那么小红第一步走哪都是赢

如果根节点是先手胜呢?

在一些情况下,小红第一步可以不执行最优策略,但他也能赢!

走先手必胜子树能保证胜利

走红必胜子树会小红导致被翻盘

走蓝必胜子树呢?

当且仅当蓝必胜子树数量-红必胜子树数量=1时,可以走蓝必胜子树

在这种情况下,如果小红往蓝必胜子树内填数,那么小蓝也必须跟着往这棵蓝必胜子树里面填树,小蓝才能保持这个子树是蓝必胜的,否则小蓝会被翻盘

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #define N1 100010
  5 using namespace std;
  6 
  7 template <typename _T> void read(_T &ret)
  8 {
  9     ret=0; _T fh=1; char c=getchar();
 10     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
 11     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
 12     ret=ret*fh;
 13 }
 14 
 15 struct Edge{
 16 int to[N1*2],nxt[N1*2],head[N1],cte;
 17 void ae(int u,int v)
 18 { cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte; }
 19 }e;
 20 
 21 int n,cnt;
 22 int f[N1],fa[N1],a[N1],ans[N1];
 23 
 24 void init()
 25 {
 26     memset(f,0,sizeof(f)); memset(&e,0,sizeof(e)); 
 27     memset(ans,0,sizeof(ans)); cnt=0;
 28 }
 29 
 30 void dfs(int x)
 31 {
 32     int j,v,sum=0;
 33     for(j=e.head[x];j;j=e.nxt[j])
 34     {
 35         v=e.to[j]; dfs(v);
 36         sum+=f[v];
 37     }
 38     if(!e.head[x]) f[x]=a[x];
 39     else{ if(sum>0) f[x]=1; else if(sum<0) f[x]=-1; else f[x]=0; }
 40 }
 41 
 42 void dfs_ans1(int x)
 43 {
 44     int j,v;
 45     for(j=e.head[x];j;j=e.nxt[j])
 46     {
 47         v=e.to[j];
 48         dfs_ans1(v);
 49     }
 50     if(!e.head[x] && !a[x]) ans[++cnt]=x;
 51 }
 52 
 53 void dfs_ans0(int x)
 54 {
 55     int j,v,sum=0;
 56     for(j=e.head[x];j;j=e.nxt[j])
 57     {
 58         v=e.to[j];
 59         sum+=f[v];
 60     }
 61     if(sum!=0 && sum!=1) return;
 62     for(j=e.head[x];j;j=e.nxt[j])
 63     {
 64         v=e.to[j];
 65         dfs_ans0(v);
 66     }
 67     if(!e.head[x] && !a[x]) ans[++cnt]=x;
 68 }
 69 
 70 void debug(int x)
 71 {
 72     while(x) printf("%d:%d\n",x,f[x]), x=fa[x];
 73     puts("");
 74 }
 75 
 76 void deson(int x)
 77 {
 78     int j,v;
 79     for(j=e.head[x];j;j=e.nxt[j])
 80     {
 81         v=e.to[j];
 82         printf("%d:%d\n",v,f[v]); 
 83     }
 84 }
 85 
 86 void solve()
 87 {
 88     int i,j; init();
 89     scanf("%d",&n); 
 90     read(fa[1]);
 91     for(i=2;i<=n;i++) read(fa[i]), e.ae(fa[i],i); // e.ae(fa[i],i);
 92     for(i=1;i<=n;i++)
 93     {
 94         read(a[i]);
 95         if(a[i]==-1) a[i]=0; else if(a[i]==0) a[i]=-1; else if(a[i]==1) a[i]=1; 
 96     }
 97     dfs(1);
 98     if(f[1]==0||f[1]==-1){
 99         if(f[1]) dfs_ans1(1);
100         else dfs_ans0(1);
101         printf("%d ",cnt);
102         sort(ans+1,ans+cnt+1);
103         for(i=1;i<=cnt;i++) printf("%d ",ans[i]);
104         puts("");
105     }else puts("-1");
106 }
107 
108 int T;
109 int main()
110 {
111     freopen("ah.in","r",stdin);
112     freopen("ah.out","w",stdout);
113     scanf("%d",&T);
114     for(int t=1;t<=T;t++) solve();
115     return 0;
116 }
View Code

 

 

 

2019 3 30 T2 起名字 (树套树)

题目大意:有$n$个硬币,正反两面权值分别为$a_{i},b_{i}$,现在有$m$次操作,每次选择一段区间$[L,R]$,把区间内朝上一面权值不大于$T_{j}$的硬币翻转,求最终所有硬币的权值和

考场上想出分情况讨论了..然而并没有想出如何应用这个策略

因为并不是动态询问硬币的权值和,所以我们暴力枚举每一枚硬币,讨论它最后是那个面朝上就行啦

假设$A<B$,那么操作分为三种,$(0,A),[A,B),[B,inf)$

第一种操作:没卵用

第二种操作:把$A$变成$B$

第三种操作:不管你是$A$还是$B$都翻面

我们只需要关心最后一次第二种操作出现的时间是什么,再在这个时间以后统计第三种操作的次数就好了!!

考虑$L=1,R=n$的情况,好像可以离线+线段树搞搞,虽然思路和正解完全不一样..

加上$L$和$R$的限制之后,需要加一个维度

我们从左到右枚举每一个硬币,插入/删除对当前硬币有影响的操作,可以用排序+堆实现。

这样我们成功消去了一维,还剩下两维,操作时间和操作权值$T$

考虑使用树套树,外层线段树内层平衡树

外层树:操作标号。内层树:权值。

第二种操作就是在线段树上二分查存在性,然后区间查一个数的排名

插入/删除操作就是单点修改,在经过的几个平衡树里把对应节点插入/删除

时间复杂度$O(nlog^{2}n)$

  1 #include <queue>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define N1 100010
  6 #define S1 N1*4
  7 #define ll long long 
  8 using namespace std;
  9 
 10 template <typename _T> void read(_T &ret)
 11 {
 12     ret=0; _T fh=1; char c=getchar();
 13     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
 14     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
 15     ret=ret*fh;
 16 }
 17 int de;
 18 
 19 struct Splay{
 20 #define M1 N1*20
 21 int ch[M1][2],fa[M1],val[M1],sz[M1],root[S1],tot; 
 22 int idf(int x){ return ch[fa[x]][0]==x?0:1; }
 23 void pushup(int x){ sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1; }
 24 void rot(int x)
 25 {
 26     int y=fa[x], ff=fa[y], px=idf(x), py=idf(y);
 27     fa[ch[x][px^1]]=y; ch[y][px]=ch[x][px^1]; 
 28     fa[y]=x; ch[x][px^1]=y; fa[x]=ff; ch[ff][py]=x;
 29     pushup(y); pushup(x);
 30 }
 31 void splay(int x,int to,int id)
 32 {
 33     if(to==root[id]) root[id]=x;
 34     int y; to=fa[to];
 35     while(fa[x]!=to)
 36     {
 37         y=fa[x];
 38         if(fa[y]==to) rot(x);
 39         else if(idf(x)==idf(y)) rot(y), rot(x);
 40         else rot(x), rot(x);
 41     }
 42 }
 43 int ins(int i,int w)
 44 {
 45     tot++; val[tot]=w; sz[tot]=1; 
 46     int x=root[i],p; 
 47     if(!x){ root[i]=tot; return tot; }
 48     while(1)
 49     {
 50         p=(w<val[x]?0:1);
 51         if(!ch[x][p]){ ch[x][p]=tot; fa[tot]=x; break; }
 52         x=ch[x][p];
 53     }
 54     splay(tot,root[i],i);
 55     return tot;
 56 }
 57 void des(int x){ ch[x][0]=ch[x][1]=fa[x]=val[x]=sz[x]=0; }
 58 void del(int i,int x)
 59 {
 60     splay(x,root[i],i);
 61     if(!ch[x][1]){
 62         root[i]=ch[x][0]; 
 63     }else{
 64         int y=ch[x][1]; root[i]=y;
 65         while(ch[y][0]) y=ch[y][0];
 66         ch[y][0]=ch[x][0]; fa[ch[x][0]]=y; //pushup(y);
 67         splay(y,root[i],i);
 68     }
 69     des(x);
 70 }
 71 int qrank(int i,int w)
 72 {
 73     int x=root[i],ans=0;
 74     while(x)
 75     {
 76         if(w<=val[x]){
 77             ans+=sz[ch[x][1]]; 
 78             if(w<=val[x]) ans++;
 79             if(!ch[x][0]){ splay(x,root[i],i); break; }
 80             x=ch[x][0];
 81         }else{
 82             if(!ch[x][1]){ splay(x,root[i],i); break; }
 83             x=ch[x][1];
 84         }
 85     }
 86     return ans;
 87 }
 88 #undef M1
 89 }sp;
 90 
 91 struct SEG{
 92 int id[N1][18];
 93 void ins(int x,int l,int r,int rt,int D,int w)
 94 {
 95     id[x][D]=sp.ins(rt,w);
 96     if(l==r) return;
 97     int mid=(l+r)>>1;
 98     if(x<=mid) ins(x,l,mid,rt<<1,D+1,w);
 99     else ins(x,mid+1,r,rt<<1|1,D+1,w);
100 }
101 void del(int x,int l,int r,int rt,int D)
102 {
103     sp.del(rt,id[x][D]);
104     if(l==r) return;
105     int mid=(l+r)>>1;
106     if(x<=mid) del(x,l,mid,rt<<1,D+1);
107     else del(x,mid+1,r,rt<<1|1,D+1);
108 }
109 int qdic(int l,int r,int rt,int ql,int qr)
110 {
111     if(l==r) 
112     {
113         if(l!=1) return l;
114         return sp.qrank(rt,ql)-sp.qrank(rt,qr+1)>0;
115     }
116     int mid=(l+r)>>1;
117     if(sp.qrank(rt<<1|1,ql)-sp.qrank(rt<<1|1,qr+1)>0)
118         return qdic(mid+1,r,rt<<1|1,ql,qr);
119     return qdic(l,mid,rt<<1,ql,qr);
120 }
121 int qsum(int L,int R,int l,int r,int rt,int limit)
122 {
123     if(L<=l&&r<=R) return sp.qrank(rt,limit);
124     int mid=(l+r)>>1, ans=0;
125     if(L<=mid) ans+=qsum(L,R,l,mid,rt<<1,limit);
126     if(R>mid) ans+=qsum(L,R,mid+1,r,rt<<1|1,limit);
127     return ans;
128 }
129 }seg;
130 
131 struct OP{ 
132 int l,r,t,w;
133 friend bool operator < (const OP &s1,const OP &s2)
134 {
135     if(s1.r!=s2.r) return s1.r>s2.r;
136     return s1.t<s2.t;
137 }
138 }op[N1];
139 int cmp(const OP &s1,const OP &s2)
140 {
141     if(s1.l!=s2.l) return s1.l<s2.l;
142     return s1.t<s2.t;
143 }
144 priority_queue<OP>que;
145 
146 int n,m;
147 int a[N1][2];
148 
149 int main()
150 {
151     freopen("name.in","r",stdin);
152     freopen("name.out","w",stdout);
153     int i,j,k,l,r,t,w,type,tmp,sum; OP K; ll ans=0;
154     scanf("%d",&n);
155     for(i=1;i<=n;i++) read(a[i][0]);
156     for(i=1;i<=n;i++) read(a[i][1]);
157     scanf("%d",&m);
158     for(i=1;i<=m;i++) read(op[i].l), read(op[i].r), read(op[i].w), op[i].t=i;
159     sort(op+1,op+m+1,cmp);
160     for(i=1,k=1;i<=n;i++)
161     {
162         for(;op[k].l==i;k++) 
163         {
164             seg.ins(op[k].t,1,m,1,0,op[k].w);
165             que.push(op[k]);
166         }
167         if(a[i][0]==a[i][1]) tmp=a[i][0]; 
168         else if(a[i][0]>a[i][1]){
169             swap(a[i][0],a[i][1]);
170             j=seg.qdic(1,m,1,a[i][0],a[i][1]-1);
171             if(j<m) sum=seg.qsum(j+1,m,1,m,1,a[i][1]); else sum=0;
172             tmp=(sum&1)?a[i][0]:a[i][1];
173         }else{
174             j=seg.qdic(1,m,1,a[i][0],a[i][1]-1);
175             if(j<m) sum=seg.qsum(j+1,m,1,m,1,a[i][1]); else sum=0;
176             if(!j) tmp=(sum&1)?a[i][1]:a[i][0];
177             else tmp=(sum&1)?a[i][0]:a[i][1];
178         }
179         ans+=tmp;
180         while(!que.empty())
181         {
182             K=que.top(); if(K.r!=i) break;
183             que.pop(); seg.del(K.t,1,m,1,0);
184         }
185     }
186     printf("%lld\n",ans);
187     return 0;
188 }
View Code

 

 

 

2019 4 3 T2 点对游戏 (概率+点分)

题目大意:三个人轮流在$n$个节点树上等概率地选取一个未被占有的节点归为己有。当取完树上所有的节点以后,开始计算的得分。一个人的得分为其占有的节点中,点对的距离为幸运数的点对数量。幸运数数量$m\leq 10,n\leq 5*10^{4}$

考场上并没有发现概率的结论..

如果一个人在树上选择了$x$个节点,那么有$C_{n}^{x}$种选择方案

在这些方案中,他占据某个点对的方案有$C_{n-2}^{x-2}$种

那么他占据这个点对的概率就是$\frac{C_{n-2}^{x-2}}{C_{n}^{x}}$

那其他人呢?打表发现一个人占据某个点对的概率并不会受其他人影响!#喷血

有了上述结论以后,现在我们只需要求出长度为幸运数的链的数量,上点分治即可

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #define N1 50010
  5 #define dd double
  6 #define ll long long 
  7 using namespace std;
  8 const int inf=0x3f3f3f3f;
  9 
 10 template <typename _T> void read(_T &ret)
 11 {
 12     ret=0; _T fh=1; char c=getchar();
 13     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
 14     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
 15     ret=ret*fh;
 16 }
 17 
 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 
 24 int n,m,SZ,G;
 25 int usedroot[N1],sz[N1],ms[N1],dep[N1],lucky[12];
 26 int que[N1],tl,bar[N1],stk[N1],tp,use[N1];
 27 ll sum[12];
 28 
 29 void gra(int x,int dad)
 30 {
 31     int j,v; sz[x]=1; ms[x]=0;
 32     for(j=e.head[x];j;j=e.nxt[j])
 33     {
 34         v=e.to[j]; if(v==dad || usedroot[v]) continue;
 35         gra(v,x); sz[x]+=sz[v]; ms[x]=max(ms[x],sz[v]);
 36     }
 37     ms[x]=max(ms[x],SZ-sz[x]);
 38     if(ms[x]<ms[G]) G=x;
 39 }
 40 void dfs_add(int x,int dad)
 41 {
 42     int j,v; que[++tl]=x;
 43     for(j=e.head[x];j;j=e.nxt[j])
 44     {
 45         v=e.to[j]; if(v==dad || usedroot[v]) continue;
 46         dep[v]=dep[x]+1; dfs_add(v,x);
 47     }
 48 }
 49 void calc(int x)
 50 {
 51     int j,v,i,k; 
 52     dep[x]=0; bar[0]=1;
 53     for(j=e.head[x];j;j=e.nxt[j])
 54     {
 55         v=e.to[j]; if(usedroot[v]) continue;
 56         dep[v]=1; dfs_add(v,x);
 57         for(i=1;i<=tl;i++)
 58         {
 59             for(k=1;k<=m;k++) if(lucky[k]-dep[que[i]]>=0)
 60                 sum[k]+=bar[lucky[k]-dep[que[i]]];
 61         }
 62         for(i=1;i<=tl;i++) 
 63         {
 64             if(!use[dep[que[i]]]) stk[++tp]=dep[que[i]], use[dep[x]]=1;
 65             bar[dep[que[i]]]++;
 66         }
 67         tl=0;
 68     }
 69     while(tp) bar[stk[tp]]=use[stk[tp]]=0, stk[tp]=0, tp--; bar[0]=0;
 70 }
 71 void main_dfs(int x)
 72 {
 73     int j,v; usedroot[x]=1; calc(x);
 74     for(j=e.head[x];j;j=e.nxt[j])
 75     {
 76         v=e.to[j]; if(usedroot[v]) continue;
 77         G=0; SZ=sz[v]; gra(v,x); 
 78         main_dfs(G);
 79     }
 80 }
 81 
 82 int main()
 83 {
 84     freopen("game.in","r",stdin);
 85     freopen("game.out","w",stdout);
 86     int i,j,x,y;
 87     scanf("%d%d",&n,&m);
 88     for(i=1;i<=m;i++) read(lucky[i]);
 89     for(i=1;i<n;i++) read(x), read(y), e.ae(x,y), e.ae(y,x);
 90     ms[0]=inf; SZ=n; gra(1,0); main_dfs(G); 
 91     dd ret;
 92     ret=0; x=n/3+(n%3>=1);
 93     for(i=1;i<=m;i++) ret+=1.0*x/n*(x-1)/(n-1)*sum[i];
 94     printf("%.2lf\n",ret);
 95     ret=0; x=n/3+(n%3>=2);
 96     for(i=1;i<=m;i++) ret+=1.0*x/n*(x-1)/(n-1)*sum[i];
 97     printf("%.2lf\n",ret);
 98     ret=0; x=n/3;
 99     for(i=1;i<=m;i++) ret+=1.0*x/n*(x-1)/(n-1)*sum[i];
100     printf("%.2lf\n",ret);
101     return 0;
102 }
View Code

 

 

 

2019 4 3 T3  (状压DP)

题目描述:

一个$ n*m $的棋盘,左上角为$ (1,1)$, 右下角为$ (n,m) $。相邻的 2点之间有连边 (如下图中实线)特殊地,(如下图中实线)特殊地, $(1,i)$ 与$(n,i)$ 也连有一条边(如下图中虚线) 也连有一条边(如下图中虚线) ,i=1..m 。
如下图,就是一个 $n=3,m=4 $的

每个点$ (i,j)$ 给定 2个值$ a[i][j],b[j] $。每条边 $e$给定 1个值 $c[e]$ 。

你的任务是给每一个点非负$d$值,最小化$ (S1+S2)$ 。

$S1=\sum_{i=1}^{n}\sum_{j=1}^{m}(d_{ij}\;xor\;a_{ij})b_{ij}$

$S2=\sum_{e}(d_{u}\;xor\;d_{v})c_{e}$

 

会发现都是异或之后再乘,所以位和位之间不会影响,我们对每一位单独处理

把棋盘斜着翻转一下,发现每一行内的点很少,状压DP?

每个点会对它上面和左面以及它自己产生贡献,最后一个格子还和第一个点产生贡献,可以按行进行转移

还可以尝试像插头DP一样按格转移,状态里记录轮廓线,会好写很多

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N1 10500
 5 #define M1 (1<<5)+1
 6 #define dd double
 7 #define ll long long 
 8 using namespace std;
 9 const ll linf=0x3f3f3f3f3f3f3f3f;
10 const int maxn=(1<<20)-1;
11 
12 template <typename _T> void read(_T &ret)
13 {
14     ret=0; _T fh=1; char c=getchar();
15     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
16     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
17     ret=ret*fh;
18 }
19 
20 int n,m;
21 int a[N1][6],b[N1][6];
22 int c0[N1][6],c1[N1][6];
23 ll f[N1][6][M1];
24 
25 int main()
26 {
27     freopen("chessboard.in","r",stdin);
28     freopen("chessboard.out","w",stdout);
29     int i,j,s,k,u,l,p,w; ll ans,tmp;
30     scanf("%d%d",&n,&m);
31     for(i=0;i<n;i++) for(j=1;j<=m;j++) read(a[j][i]);
32     for(i=0;i<n;i++) for(j=1;j<=m;j++) read(b[j][i]);
33     for(i=0;i<n;i++) for(j=1;j<m;j++) read(c0[j][i]);
34     for(i=0;i<n-1;i++) for(j=1;j<=m;j++) read(c1[j][i]);
35     for(j=1;j<=m;j++) read(c1[j][n-1]);
36     
37     for(k=0,ans=0;k<20;k++) {
38     
39     memset(f,0x3f,sizeof(f));
40     f[0][n-1][0]=0;
41     for(i=1;i<=m;i++)
42     {
43         for(j=0,s=0;s<(1<<n);s++)
44         {
45             if(f[i-1][n-1][s]==linf) continue;
46             u=(s>>j)&1; w=(a[i][j]>>k)&1;
47             p=0;
48             f[i][j][s&(maxn^1)] = min(f[i][j][(s&(maxn^1))], f[i-1][n-1][s]+(p^u)*c0[i-1][j]+(p^w)*b[i][j] );
49             p=1;
50             f[i][j][s|1] = min(f[i][j][s|1], f[i-1][n-1][s]+(p^u)*c0[i-1][j]+(p^w)*b[i][j] );
51         }
52         for(j=1;j<n;j++)
53         {
54             w=(a[i][j]>>k)&1;
55             for(s=0;s<(1<<n);s++)
56             {
57                 if(f[i][j-1][s]==linf) continue;
58                 l=(s>>(j-1))&1; u=(s>>j)&1;
59                 p=0;
60                 f[i][j][s&(maxn^(1<<j))] = min(f[i][j][s&(maxn^(1<<j))], f[i][j-1][s]+(p^u)*c0[i-1][j]+(p^l)*c1[i][j-1]+(p^w)*b[i][j]+((j!=n-1)?0:(p^(s&1))*c1[i][j]) );
61                 p=1;
62                 f[i][j][s|(1<<j)] = min(f[i][j][s|(1<<j)], f[i][j-1][s]+(p^u)*c0[i-1][j]+(p^l)*c1[i][j-1]+(p^w)*b[i][j]+((j!=n-1)?0:(p^(s&1))*c1[i][j]) );
63             }
64         }
65     }
66     for(s=0,tmp=linf;s<(1<<n);s++) tmp=min(tmp,(1ll*f[m][n-1][s]<<k));
67     ans+=tmp;
68     
69     }
70     printf("%lld\n",ans);
71     return 0;
72 }
View Code

 

posted @ 2019-03-28 22:33  guapisolo  阅读(71)  评论(0)    收藏  举报