题目链接: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\n",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\n");
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\n",dp[k][m]);
64     }
65 }
分组背包

 

  E题,直接暴力枚举即可。完全背包也行。第一次wa是因为认为贪心先拿最小的最优,其实不然。例如:53--10,11,23。显然11的拿3个再拿10的两个比较好。