国际惯例的题面:
首先这题是缺少两个隐藏条件的:第一个是这k条链的起点和终点所在的层是对齐的,也就是说不会出现两条链错开的情况,且这张图恰好由n层组成;第二个就是任意一个点都包含在与链上的点直接或间接相关的同一层中,不存在与链无关的孤立层。(如果缺少第一个条件这题是基本没法做的,分类讨论细节太多;第二个讨论倒是无关紧要,你钦定孤立层的点都在第一层就好了)(别问我怎么知道的,第一个是找学长问出来的,第二个是调?教!某神犇的AC代码(把他WA掉)试出来的)然后就是怎么做的问题。观察到那个ln十分不好处理,我们可以枚举它。反正ln增长很慢,总共就40+种取值。同时发现k很小,这提醒我们,每层的关键点很少,我们能否状压呢?我们先枚举那个ln的值,定义f[depth][statement][different]表示考虑从第1到depth层,当前层(第depth层)修改状态为statement,总共产生的链上相邻点修改状态不同的数量为different的最大收益。f[depth][statement][different] = max( f[depth-1][last_statement][different-diff(statement^last_statement)] + calc(depth,statement) )。考虑如何计算第depth层,状态为statement时的最大代价。点要么改要么不改,二种状态必选其一,不是2-sat就是最小割。而2-sat只能解决判定性问题,所以铁定是最小割了。考虑怎么建图:(参考"Bzoj3438: 小M的作物"和"3894: 文理分科"的建图,然而你都没有写过也没关系,我会叙述清楚)首先我们累加能获得的所有权值,再减去最后的最小割。我们钦定对于一个点,如果在最小割中与源点相连,代表它保持原状,与汇点相连代表它被改变性别。那么这个点与源点的边权就是对它进行改变的代价,与汇点的边权就是让它维持原状的代价。(对于链上的点(显然状态被statement确定),我们让它只与源点汇点连inf边表示它被钦定保持原状/进行改变)对于一条原图中的边(就是"繁衍关系"),在两个点都不被改变时产生价值di,我们为可以为它新建一个点,从源点向这个点连容量di的边,再从这个点向依赖的两个点ui,vi连接容量为inf的边,代表如果要把ui或者vi放到与汇点相连的集合,则必须割掉这条边的权值。两个点都改变时产生的价值di*bi建图同理。然后就是一些细节,比如层内的代价只和枚举的ln,层数depth和状态statement有关,那么我们对这样的三元组可以只跑一次网络流而不是每次进行计算。网络流重建图的时候不要memset整个数组,用多少memset多少,否则TLE。对每个点转换一下坐标,把点的编号转化为它在层内的编号,这样能使得每次网络流点的编号连续,减少memset的时间。(然后就是我人傻用了一堆vector导致大常数的代码了)代码:1 #pragma GCC optimize(2) 2 #include3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #define debug cout 10 using namespace std; 11 const int maxn=1e3+1e2,maxe=5e4+1e3,maxs=1<<4; 12 const int inf=0x3f3f3f3f,minf=0xefefefef; 13 14 int chain[5][maxn],id[maxn],c[maxn],dep[maxn],rid[maxn]; // id of chain . 15 vector pts[maxn]; // points of each level . 16 struct Edge { int u,v,b,d;}ine[maxe]; 17 vector es[maxn]; // edge of each level. 18 int f[55][1<<4][210],ln[210]; // f[level][statement][diffs] 19 int n,m,k,p,full,ans; 20 21 namespace Flow { 22 int s[maxe],t[maxe<<2],nxt[maxe<<2],f[maxe<<2],cnt=1; 23 int dep[maxe],st,ed; 24 inline void coredge(int from,int to,int flow) { 25 t[++cnt] = to , f[cnt] = flow , nxt[cnt] = s[from] , s[from] = cnt; 26 } 27 inline void singledge(int from,int to,int flow) { 28 coredge(from,to,flow) , coredge(to,from,0); 29 } 30 inline bool bfs() { 31 memset(dep,-1,sizeof(int)*(ed+1)) , dep[st] = 0; 32 queue q; q.push(st); 33 while( q.size() ) { 34 const int pos = q.front(); q.pop(); 35 for(int at=s[pos];at;at=nxt[at]) 36 if( f[at] && !~dep[t[at]] ) 37 dep[t[at]] = dep[pos] + 1 , q.push(t[at]); 38 } 39 return ~dep[ed]; 40 } 41 inline int dfs(int pos,int flow) { 42 if( pos == ed ) return flow; 43 int ret = 0 , now = 0; 44 for(int at=s[pos];at;at=nxt[at]) 45 if( f[at] && dep[t[at]] > dep[pos] ) { 46 now = dfs(t[at],min(flow,f[at])) , 47 ret += now , flow -= now , f[at] -= now , f[at^1] += now; 48 if( !flow ) return ret; 49 } 50 if( !ret ) dep[pos] = -1; 51 return ret; 52 } 53 inline int dinic() { 54 int ret = 0; 55 while( bfs() ) ret += dfs(st,inf); 56 return ret; 57 } 58 inline void reset() { 59 memset(s,0,sizeof(int)*(ed+1)) , cnt = 1; 60 } 61 } 62 63 inline int build(int dep,int sta,int mul) { 64 using namespace Flow; 65 st = pts[dep].size() + ( es[dep].size() * 2 ) + 1 , ed = st + 1 , reset(); 66 #define cov(x,t) (x*2+1+t+pts[dep].size()) 67 int ret = 0; 68 for(unsigned i=0;i > ( id[p] - 1 ) ) & 1 ) singledge(rid[p],ed,inf) , ret -= c[p]; 72 else singledge(st,rid[p],inf); 73 } else singledge(st,rid[p],c[p]) , singledge(rid[p],ed,0); 74 } 75 for(unsigned i=0;i << k;136 for(int i=1;i<=m;i++) scanf("%d",c+i);137 for(int i=1;i<=k;i++) for(int j=1;j<=n;j++) scanf("%d",chain[i]+j) , id[chain[i][j]] = i;138 for(int i=1;i<=p;i++) {139 scanf("%d%d%d%lf",&ine[i].u,&ine[i].v,&ine[i].d,&dd) , ine[i].b = floor( ine[i].d * dd ) , 140 ufs.merge(ine[i].u,ine[i].v);141 }142 for(int i=1;i<=k;i++) for(int j=1;j<=n;j++) dep[ufs.findfa(chain[i][j])] = j;143 for(int i=1;i<=m;i++) {144 dep[i] = dep[ufs.findfa(i)];145 if( !dep[i] ) dep[i] = dep[ufs.findfa(i)] = 1;146 pts[dep[i]].push_back(i) , rid[i] = pts[dep[i]].size();147 }148 for(int i=1;i<=p;i++) es[dep[ine[i].u]].push_back(ine[i]);149 for(int i=ln[0];i<=ln[n*k];i++) dp(i);150 printf("%d\n",ans);151 return 0;152 }