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