题目链接:https://vjudge.net/contest/147974#overview。
A题,费用流,不会。。跳过了。
B题,给一个图,问至少添加几条边能成为强连通图。显然缩点,要使得成为一个scc,任意一个点都要至少一个入度和出度,而一条边可以提供一个入度和出度,因为答案为max(入度为0的点,出度为0的点)。如果要求最多能添加几条使得还不是scc,则参照:最多添加几条使得还不是scc。如果是无向图问至少添加几条使得是边双联通,则参照:至少添加几条使得边双联通。
C题,线段树区间合并,貌似暑假的时候写过类似的,但是忘记了。。代码如下:

1 //#include <bits/stdc++.h> 2 #include <stdio.h> 3 #include <algorithm> 4 #include <string.h> 5 #define t_mid (l+r>>1) 6 #define ls (o<<1) 7 #define rs (o<<1|1) 8 #define lson ls,l,t_mid 9 #define rson rs,t_mid+1,r 10 using namespace std; 11 const int N = 100000 + 5; 12 13 int a[N]; 14 // sub表示节点o的lcis的最长长度,l_val和r_val分别表示这个节点最左边和最右边的值分别是多少 15 // l_sub表示这个节点从最左边开始的最长的lcis的长度,r_sub同 16 int sub[N<<2],l_val[N<<2],r_val[N<<2],l_sub[N<<2],r_sub[N<<2]; 17 int T,n,q; 18 void up(int o,int l,int r) 19 { 20 int len = r - l + 1; 21 l_val[o] = l_val[ls], r_val[o] = r_val[rs]; // 更新l_val,r_val 22 // 更新sub的值 23 sub[o] = max(sub[ls], sub[rs]); 24 if(r_val[ls] < l_val[rs]) sub[o] = max(sub[o], r_sub[ls]+l_sub[rs]); 25 26 // 更新l_sub,r_sub 27 l_sub[o] = l_sub[ls]; 28 if(l_sub[o] == len-len/2 && r_val[ls] < l_val[rs]) l_sub[o] += l_sub[rs]; 29 30 r_sub[o] = r_sub[rs]; 31 if(r_sub[o] == len/2 && r_val[ls] < l_val[rs]) r_sub[o] += r_sub[ls]; 32 } 33 void build(int o,int l,int r) 34 { 35 if(l == r) 36 { 37 sub[o] = l_sub[o] = r_sub[o] = 1; 38 l_val[o] = r_val[o] = a[l]; 39 return ; 40 } 41 build(lson); 42 build(rson); 43 up(o,l,r); 44 } 45 void update(int o,int l,int r,int pos,int x) 46 { 47 if(l == r) 48 { 49 l_val[o] = r_val[o] = x; 50 return ; 51 } 52 if(pos <= t_mid) update(lson,pos,x); 53 else update(rson,pos,x); 54 up(o,l,r); 55 } 56 int query(int o,int l,int r,int ql,int qr) 57 { 58 if(l == ql && r == qr) return sub[o]; 59 int ans = 0; 60 if(qr <= t_mid) ans = query(lson,ql,qr); 61 else if(ql > t_mid) ans = query(rson,ql,qr); 62 else 63 { 64 ans = max(query(lson,ql,t_mid), query(rson,t_mid+1,qr)); 65 // 注意下面一行的两个min 66 if(r_val[ls] < l_val[rs]) ans = max(ans, min(r_sub[ls], t_mid-ql+1) + min(l_sub[rs], qr-t_mid)); 67 } 68 return ans; 69 } 70 71 int main() 72 { 73 scanf("%d",&T); 74 while(T--) 75 { 76 scanf("%d%d",&n,&q); 77 for(int i=1;i<=n;i++) scanf("%d",a+i); 78 build(1,1,n); 79 while(q--) 80 { 81 char s[5]; 82 int x,y; 83 scanf("%s%d%d",s,&x,&y); 84 if(s[0] == 'U') update(1,1,n,x+1,y); 85 else printf("%d ",query(1,1,n,x+1,y+1)); 86 } 87 } 88 return 0; 89 }
D题,分组背包,每组中,先选择一个,然后该组中其他的有三个选择,不选,或者从之前一组中转移(也就是放弃了这组中第一个选的而选这组中的这个),或者从这组中的转移(这组中的第一个要选)。代码如下:

1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <vector> 5 #include <map> 6 #include <set> 7 #include <queue> 8 #include <iostream> 9 #include <stdlib.h> 10 #include <string> 11 #include <stack> 12 using namespace std; 13 const int inf = 0x3f3f3f3f; 14 typedef long long ll; 15 typedef pair<int,int> pii; 16 const int N = 100 + 5; 17 18 int n,m,k; 19 vector<pii> v[15]; 20 int dp[15][15000+5]; 21 22 int main() 23 { 24 while(scanf("%d%d%d",&n,&m,&k) == 3) 25 { 26 for(int i=1;i<=k;i++) v[i].clear(); 27 for(int i=1;i<=n;i++) 28 { 29 int pos,mo,val; 30 scanf("%d%d%d",&pos,&mo,&val); 31 v[pos].push_back(pii(mo,val)); 32 } 33 int sum = 0; 34 int flag = 1; 35 for(int i=1;i<=k;i++) 36 { 37 if(v[i].size() == 0) 38 { 39 flag = 0; 40 break; 41 } 42 sort(v[i].begin(), v[i].end()); 43 sum += v[i][0].first; 44 } 45 if(sum > m || flag == 0) 46 { 47 printf("Impossible "); 48 continue; 49 } 50 memset(dp,0,sizeof dp); 51 for(int i=1;i<=k;i++) 52 { 53 for(int j=0;j<v[i].size();j++) 54 { 55 int w = v[i][j].first, val = v[i][j].second; 56 for(int mask=m;mask>=w;mask--) 57 { 58 if(j == 0) dp[i][mask] = dp[i-1][mask-w] + val; 59 else dp[i][mask] = max({dp[i][mask], dp[i-1][mask-w]+val, dp[i][mask-w]+val}); 60 } 61 } 62 } 63 printf("%d ",dp[k][m]); 64 } 65 }
E题,直接暴力枚举即可。完全背包也行。第一次wa是因为认为贪心先拿最小的最优,其实不然。例如:53--10,11,23。显然11的拿3个再拿10的两个比较好。