令人震撼。
 E.Decomposition
 题目描述:
对于 n 个数 x1,x2,...,xn,定义其拆分为:
从 1 到 n 的顺序遍历 x1,x2,...,xn。
设当前遍历到 xi:
设 f([x1,x2,...,xn]) 为拆分得到序列的个数。给出 a1,a2,...,an,求 ∑i=1n∑j=inf([ai,ai+1,...,aj])。
1≤n≤3⋅105,0≤ai≤3
 题目分析:
一眼看过去没啥头绪,观察到 a 的范围很小,所以考虑在这么小的范围内会有什么性质。
首先若 ai=0,则其与任何一个数的 AND 均为 0,也就是说必然会新开一个序列,所以其不会对其它的数的形成序列的情况产生影响,可以在处理其他的数的时候忽略掉 ai=0 的位置产生的序列,最后再对于每个 ai=0 的位置判断一下会造成多少影响即可。
注意到我们只有三个数,所以显然序列个数不会超过 3 个,因为最多也就是一个数占一个序列,这样的话后来的数就必然可以找到对应的序列的位置了。
当然这样并不是很严谨,因为 3 与 1,2 的按位与均不为 0 所以可能会有一些其他的情况,手模一下的话就会发现大部分情况就是只有两个序列,只有少部分情况能造出来三个序列。
因为只有三个序列所以我们可以直接 dp 记录下三个序列结尾是什么即可。
具体来说就是设 dp[i][a][b][c] 表示以 i 为右端点的区间,形成的三个序列的最后一个数分别为 a,b,c 的方案数,注意这里序列为空我们定义为最后一个数为 0,然后每次新加入一个数就暴力模拟一下加入哪里即可。
dp 的初值其实就是 dp[i][ai][0][0]=1。
时间复杂度 O(n)。
 代码:
 点击查看代码 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 
 | #include<bits/stdc++.h>#define int long long
 using namespace std;
 const int N = 5e5+5;
 int a[N];
 int dp[N][5][5][5];
 void add(int &a,int b){
 a = a + b;
 }
 signed main(){
 int n;scanf("%lld",&n);
 for(int i=1; i<=n; i++)	scanf("%lld",&a[i]);
 for(int i=0; i<n; i++){
 int p[4];
 for(p[1]=0; p[1]<=3; p[1]++){
 for(p[2]=0; p[2]<=3; p[2]++){
 for(p[3]=0; p[3]<=3; p[3]++){
 if(!dp[i][p[1]][p[2]][p[3]])	continue;
 if(!a[i+1]){
 dp[i+1][p[1]][p[2]][p[3]] = dp[i][p[1]][p[2]][p[3]];
 continue;
 }
 if((p[1] & a[i+1]) || !p[1]){
 add(dp[i+1][a[i+1]][p[2]][p[3]],dp[i][p[1]][p[2]][p[3]]);
 }
 else if((p[2] & a[i+1]) || !p[2]){
 add(dp[i+1][p[1]][a[i+1]][p[3]],dp[i][p[1]][p[2]][p[3]]);
 }
 else if((p[3] & a[i+1]) || !p[3]){
 add(dp[i+1][p[1]][p[2]][a[i+1]],dp[i][p[1]][p[2]][p[3]]);
 }
 }
 }
 }
 add(dp[i+1][a[i+1]][0][0],1);
 }
 int ans=0;
 for(int i=1; i<=n; i++){
 for(int A=0; A<=3; A++){
 for(int B=0; B<=3; B++){
 for(int C=0; C<=3; C++){
 if(dp[i][A][B][C]){
 add(ans,((A != 0) + (B != 0) + (C != 0)) * dp[i][A][B][C]);
 }
 }
 }
 }
 }
 for(int i=1; i<=n; i++){
 if(!a[i])	add(ans,i*(n-i+1));
 }
 printf("%lld\n",ans);
 return 0;
 }
 
 | 
 
 F.MCF
 题目描述:
你需要解决一个有源汇最小费用可行流问题,但是,容量与流量的奇偶性必须相同。
点数在 2∼100 范围内,边数在 1∼200 范围内,容量在 1∼100 范围内,费用在 −100∼100 范围内。
图中没有负环。
 题目分析:
最难受的也就是这个奇偶性必须相同的限制,想想怎么去了。
可以考虑先将所有容量为奇数的边强制流 1 的流量,然后这样的话我们就可以让所有的边容量变成 ⌊2c⌋,就是一个正常的最小费用可行流的问题,然后将跑出来的流量乘以 2 即可。
那么对于这些强制流的 1 该如何处理呢,我们假设只考虑这些强制流 1 的边,点 i 的流入流量减流出流量为 fi,显然若 fi 为奇数则无解,当然如果 f1,fn 为奇数也是有解的因为源汇可以不满足流量守恒。
否则的话如果 fi>0,我们就可以从 S 到 i 连流量为 2fi 费用为 −∞ 的边来强制让这个流量也流过去,所以如果最后的残量网络中,这些边还有一些剩余流量则无解。
对于 fi<0 的情况也是同理的。
但是如果按照正常可行流的做法从 T 到 S 连边的话就会造成负环比较寄,可以造一个虚拟的源汇 S′,T′,并且连边 S′→S 和 T→T′,由 S′,T′ 来承担强制流某些流量的任务即可。
最小费用可行流其实就是如果我们跑出来的最短路的费用小于 0 则增广,否则不增广。
 代码:
 点击查看代码 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 
 | #include<bits/stdc++.h>#define int long long
 using namespace std;
 const int MAXN = 200+5;
 const int MAXM = 1000+5;
 const int INF = 1e9+5;
 struct edge{
 int nxt,to,val,cost;
 edge(){}
 edge(int _nxt,int _to,int _val,int _cost){
 nxt = _nxt,to = _to,val = _val,cost = _cost;
 }
 }e[MAXM];
 int n,m,cnt = 1,flow[MAXN],dis[MAXN],pos[MAXN],mark[MAXN],head[MAXN],s,t,cur[MAXN],a[100][100];
 bool vis[MAXN],in_que[MAXN];
 bool spfa(){
 for(int i=1; i<=t; i++){
 dis[i] = INF;vis[i] =false;in_que[i]=false;cur[i]=head[i];
 }
 queue<int> q;
 q.push(s);dis[s] = 0;in_que[s] = true;
 bool flag = false;
 while(!q.empty()){
 int now = q.front();q.pop();in_que[now] = false;
 for(int i = head[now]; i;i = e[i].nxt){
 int to = e[i].to;
 if(e[i].val && dis[to] > dis[now] + e[i].cost){
 if(to == t)	flag = true;
 dis[to] = dis[now] + e[i].cost;
 if(!in_que[to]){
 q.push(to);
 in_que[to] = true;
 }
 }
 }
 }
 return flag;
 }
 int dfs(int now,int limit){
 if(now == t)	return limit;
 int flow = 0;vis[now] = true;
 for(int i = cur[now]; i && flow < limit; i = e[i].nxt){
 int to = e[i].to;
 cur[now] = i;
 if(e[i].val && dis[to] == dis[now] + e[i].cost && !vis[to]){
 int h = dfs(to,min(limit - flow,e[i].val));
 e[i].val -= h;e[i^1].val+=h;flow+=h;
 if(!h)	dis[to] = -INF;
 }
 }
 return flow;
 }
 void dinic(){
 while(spfa()){
 if(dis[t] >= 0)	break;
 dfs(s,INF);
 }
 }
 void add_edge(int from,int to,int val,int cost){
 e[++cnt] = edge(head[from],to,val,cost);
 head[from] = cnt;
 e[++cnt] = edge(head[to],from,0,-cost);
 head[to] = cnt;
 }
 signed main(){
 
 
 scanf("%lld%lld",&n,&m);
 for(int i=1; i<=m; i++){
 int from,to,val,cost;scanf("%lld%lld%lld%lld",&from,&to,&val,&cost);
 add_edge(from,to,val/2,cost);
 pos[i] = cnt;
 if(val & 1){
 mark[i] = 1;
 flow[from]--,flow[to]++;
 }
 }
 bool flag = true;
 s = n + 1,t = s + 1;
 for(int i=2; i<n; i++){
 if(flow[i] % 2 != 0)	flag = false;
 if(flow[i] > 0)	add_edge(s,i,flow[i]/2,-INF);
 if(flow[i] < 0)	add_edge(i,t,-flow[i]/2,-INF);
 }
 add_edge(s,1,INF,0);add_edge(n,t,INF,0);
 dinic();
 for(int i=2; i<=cnt; i++){
 if(e[i].cost == -INF && e[i].val != 0){
 flag = false;
 }
 }
 if(!flag){
 printf("Impossible\n");
 return 0;
 }
 printf("Possible\n");
 for(int i=1; i<=m; i++){
 printf("%lld ",e[pos[i]].val * 2 + mark[i]);
 }
 return 0;
 }
 
 |