2017ACM省赛选拔赛题解

Problem A: 聪明的田鼠

题解:

dp[k][i]表示走了k步,且在第i行的最大值

最后的结果就是走了n+m-2步,且在第n行的值

代码:

 1 #include <map>
 2 #include <set>
 3 #include <cmath>
 4 #include <queue>
 5 #include <stack>
 6 #include <cstdio>
 7 #include <string>
 8 #include <vector>
 9 #include <cstdlib>
10 #include <cstring>
11 #include <sstream>
12 #include <iostream>
13 #include <algorithm>
14 #include <functional>
15 using namespace std;
16 #define rep(i,a,n) for (int i=a;i<n;i++)
17 #define per(i,a,n) for (int i=n-1;i>=a;i--)
18 #define pb push_back
19 #define mp make_pair
20 #define all(x) (x).begin(),(x).end()
21 #define SZ(x) ((int)(x).size())
22 typedef vector<int> VI;
23 typedef long long ll;
24 typedef pair<int, int> PII;
25 const ll MOD = 1e9 + 7;
26 const int INF = 0x3f3f3f3f;
27 const double EPS = 1e-10;
28 const double PI = acos(-1.0);
29 const int MAXN = 60;
30 // head
31
32 int n, m;
33 int a[MAXN][MAXN];
34 int dp[MAXN * 2][MAXN];
35
36 int main() {
37     cin >> n >> m;
38     rep(i, 1, n + 1) rep(j, 1, m + 1) cin >> a[i][j];
39     dp[0][1] = a[1][1];
40     rep(k, 1, n + m - 1) rep(i, max(1, k + 2 - m), n + 1) if (i <= k + 1)
41         dp[k][i] = max(dp[k - 1][i], dp[k - 1][i - 1]) + a[i][k + 2 - i];
42     cout << dp[n + m - 2][n] << endl;
43     return 0;
44 }

Problem B: 软件安装

题解:

线段树区间更新,不太好写,先附上某人的代码

代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N=5e5+7;
 5 int read(){
 6     int x=0,f=1;char ch=getchar();
 7     while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
 8     while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar();
 9     return x*f;
10 }
11 #define lson (rt<<1)
12 #define rson (rt<<1|1)
13 int msum[N<<2],lsum[N<<2],rsum[N<<2],len[N<<2],tag[N<<1],n,m;
14 void pushup(int rt){
15     lsum[rt]=lsum[lson];
16     rsum[rt]=rsum[rson];
17     if(lsum[lson]==len[lson]){lsum[rt]=lsum[lson]+lsum[rson];}
18     if(rsum[rson]==len[rson]){rsum[rt]=rsum[rson]+rsum[lson];}
19     msum[rt]=max(max(msum[lson],msum[rson]),max(lsum[rt],rsum[rt]));
20     msum[rt]=max(msum[rt],rsum[lson]+lsum[rson]);
21 }
22 void pushdown(int rt){
23     int t=tag[rt];
24     if(t!=-1){
25         msum[lson]=lsum[lson]=rsum[lson]=len[lson]*t;
26         msum[rson]=lsum[rson]=rsum[rson]=len[rson]*t;
27         tag[rson]=tag[lson]=t;
28         tag[rt]=-1;
29     }
30 }
31 void build(int rt,int l,int r){
32     if(l>r)return;
33     msum[rt]=lsum[rt]=rsum[rt]=len[rt]=r-l+1;tag[rt]=-1;
34     if(l==r)return;
35     int mid=l+r>>1;
36     build(lson,l,mid);build(rson,mid+1,r);
37 }
38 void modify(int rt,int l,int r,int a,int b,int val){
39     if(a<=l&&r<=b){msum[rt]=rsum[rt]=lsum[rt]=val*len[rt];tag[rt]=val;return;}
40     pushdown(rt);
41     int mid=l+r>>1;
42     if(a<=mid)modify(lson,l,mid,a,b,val);
43     if(b>mid)modify(rson,mid+1,r,a,b,val);
44     pushup(rt);
45 }
46 int query(int rt,int l,int r,int k){
47     pushdown(rt);
48     int mid=l+r>>1;
49     if(msum[lson]>=k)return query(lson,l,mid,k);
50     else if(rsum[lson]+lsum[rson]>=k)return mid-rsum[lson]+1;
51     else return query(rson,mid+1,r,k);
52 }
53 int main(){
54     n=read();m=read();
55     build(1,1,n);
56     while(m--){
57         int op=read(),a=read(),b;
58         if(op==1){
59             if(msum[1]<a)puts("0");
60             else{
61                 int p=query(1,1,n,a);
62                 printf("%d\n",p);
63                 modify(1,1,n,p,p+a-1,0);
64             }
65         }else{
66             b=read();
67             modify(1,1,n,a,a+b-1,1);
68         }
69     }
70 }

Problem C: V型积木

题解:

最长上升子序列,枚举每个最为最低点,分别求左右两边的LIS,复杂度n3

不过学长说可以用树状数组,nlogn就能解决,万能的树状数组。。

代码:

 1 #include <map>
 2 #include <set>
 3 #include <cmath>
 4 #include <queue>
 5 #include <stack>
 6 #include <cstdio>
 7 #include <string>
 8 #include <vector>
 9 #include <cstdlib>
10 #include <cstring>
11 #include <sstream>
12 #include <iostream>
13 #include <algorithm>
14 #include <functional>
15 using namespace std;
16 #define rep(i,a,n) for (int i=a;i<n;i++)
17 #define per(i,a,n) for (int i=n-1;i>=a;i--)
18 #define pb push_back
19 #define mp make_pair
20 #define all(x) (x).begin(),(x).end()
21 #define SZ(x) ((int)(x).size())
22 typedef vector<int> VI;
23 typedef long long ll;
24 typedef pair<int, int> PII;
25 const ll MOD = 1e9 + 7;
26 const int INF = 0x3f3f3f3f;
27 const double EPS = 1e-10;
28 const double PI = acos(-1.0);
29 const int MAXN = 110;
30 // head
31
32 int n;
33 int a[MAXN];
34 int dp[MAXN];
35
36 int main() {
37     int T;
38     cin >> T;
39     while (T--) {
40         cin >> n;
41         rep(i, 0, n) scanf("%d", a + i);
42         int ans = 0;
43         rep(k, 0, n) {
44             memset(dp, 0, sizeof(dp));
45             int sum = 0, t = 0;
46             per(i, 0, k) rep(j, i + 1, k + 1) if (a[i] > a[j])
47                 dp[i] = max(dp[i], dp[j] + 1), t = max(t, dp[i]);
48             if (t == 0) continue;
49             sum += t;
50             t = 0;
51             rep(i, k + 1, n) rep(j, k, i) if (a[i] > a[j])
52                 dp[i] = max(dp[i], dp[j] + 1), t = max(t, dp[i]);
53             if (t == 0) continue;
54             sum += t;
55             ans = max(ans, sum + 1);
56         }
57         if (ans == 0) cout << "No Solution" << endl;
58         else cout << n - ans << endl;
59     }
60     return 0;
61 }

Problem D: 最佳地址

题解:

最小费用流,建立一个超级源点s和超级汇点t

coal0表示原有的发电厂,coal1表示当前要新建的发电厂

从s到coal0连一条流量为b,费用为0的边

从s到coal1连一条流量为sum-b,费用为0的边  因为题目说了全部供应,所以剩下的所有煤矿都要供应到新发电厂

然后coal0和coal1都分别和每个煤矿相连,流量就是煤矿的产量a[i],费用就是c[0][i]和c[1][i]

最后每个煤矿再连接到t,流量为a[i],费用为0

然后跑一下最小费用流就可以了

点的标号分别为0-m-1为煤矿   m为coal0   m+1为coal1   m+2为s   m+3为t   一共m+4个点

代码:

  1 #include <map>
  2 #include <set>
  3 #include <cmath>
  4 #include <queue>
  5 #include <stack>
  6 #include <cstdio>
  7 #include <string>
  8 #include <vector>
  9 #include <cstdlib>
 10 #include <cstring>
 11 #include <sstream>
 12 #include <iostream>
 13 #include <algorithm>
 14 #include <functional>
 15 using namespace std;
 16 #define rep(i,a,n) for (int i=a;i<n;i++)
 17 #define per(i,a,n) for (int i=n-1;i>=a;i--)
 18 #define pb push_back
 19 #define mp make_pair
 20 #define all(x) (x).begin(),(x).end()
 21 #define SZ(x) ((int)(x).size())
 22 typedef vector<int> VI;
 23 typedef long long ll;
 24 typedef pair<int, int> PII;
 25 const ll MOD = 1e9 + 7;
 26 const int INF = 0x3f3f3f3f;
 27 const double EPS = 1e-10;
 28 const double PI = acos(-1.0);
 29 const int MAXN = 110;   //看了测试数据,m和n最大不超过100
 30 // head
 31
 32 int n, b, m, H;
 33 int a[MAXN];
 34 int h[MAXN];
 35 int c[MAXN][MAXN];
 36
 37 struct edge { int to, cap, cost, rev; };
 38 const int MAX_V = 1e4 + 7;
 39 int V;
 40 vector<edge> G[MAX_V];
 41 int dist[MAX_V];
 42 int prevv[MAX_V], preve[MAX_V];
 43
 44 void add_edge(int from, int to, int cap, int cost) {
 45     G[from].pb(edge{ to,cap,cost,(int)G[to].size() });
 46     G[to].pb(edge{ from,0,-cost,(int)G[from].size() - 1 });
 47 }
 48
 49 int min_cost_flow(int s, int t, int f) {
 50     int res = 0;
 51     while (f > 0) {
 52         memset(dist, 0x3f, sizeof(dist));
 53         dist[s] = 0;
 54         bool update = true;
 55         while (update) {
 56             update = false;
 57             rep(v, 0, V) {
 58                 if (dist[v] == INF) continue;
 59                 rep(i, 0, G[v].size()) {
 60                     edge &e = G[v][i];
 61                     if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
 62                         dist[e.to] = dist[v] + e.cost;
 63                         prevv[e.to] = v;
 64                         preve[e.to] = i;
 65                         update = true;
 66                     }
 67                 }
 68             }
 69         }
 70         if (dist[t] == INF) return -1;
 71         int d = f;
 72         for (int v = t; v != s; v = prevv[v]) d = min(d, G[prevv[v]][preve[v]].cap);
 73         f -= d;
 74         res += d*dist[t];
 75         for (int v = t; v != s; v = prevv[v]) {
 76             edge &e = G[prevv[v]][preve[v]];
 77             e.cap -= d;
 78             G[v][e.rev].cap += d;
 79         }
 80     }
 81     return res;
 82 }
 83
 84 int main() {
 85     int T;
 86     cin >> T;
 87     while (T--) {
 88         cin >> m >> b >> H >> n;
 89         int sum = 0;
 90         rep(i, 0, m) scanf("%d", a + i), sum += a[i];
 91         rep(i, 0, n) scanf("%d", h + i);
 92         rep(i, 0, m) scanf("%d", &c[0][i]);
 93         PII ans = mp(0, INF);
 94         rep(k, 0, n) {
 95             rep(i, 0, MAX_V) G[i].clear();
 96             rep(i, 0, m) scanf("%d", &c[1][i]);
 97             int coal0 = m, coal1 = m + 1, s = m + 2, t = m + 3;
 98             V = m + 4;
 99             add_edge(s, coal0, b, 0);
100             add_edge(s, coal1, sum - b, 0);
101             rep(i, 0, m) {
102                 add_edge(coal0, i, a[i], c[0][i]);
103                 add_edge(coal1, i, a[i], c[1][i]);
104                 add_edge(i, t, a[i], 0);
105             }
106             int mcf = min_cost_flow(s, t, sum) + H + h[k];
107             if (mcf < ans.second) ans.second = mcf, ans.first = k;
108         }
109         cout << ans.first + 1 << ‘ ‘ << ans.second << endl;
110     }
111     return 0;
112 }

Problem E: 寻宝

题解:

从0点(题目中的1,习惯从0开始,所以全都-1计算)跑一下改进版dijkstra

当然求的不是最短路,d[i]表示从0到i的所有路线中 每条路线中的权重最小的值  的最大值

这个只要稍微修改一下就好了

c[i]存的就是有c[i]个宝藏地点 可以携带i个宝藏到达那里,因为最多有k个宝藏,所以超过k的也按k算

最后贪心模拟一下就好了

代码:

 1 #include <map>
 2 #include <set>
 3 #include <cmath>
 4 #include <queue>
 5 #include <stack>
 6 #include <cstdio>
 7 #include <string>
 8 #include <vector>
 9 #include <cstdlib>
10 #include <cstring>
11 #include <sstream>
12 #include <iostream>
13 #include <algorithm>
14 #include <functional>
15 using namespace std;
16 #define rep(i,a,n) for (int i=a;i<n;i++)
17 #define per(i,a,n) for (int i=n-1;i>=a;i--)
18 #define pb push_back
19 #define mp make_pair
20 #define all(x) (x).begin(),(x).end()
21 #define SZ(x) ((int)(x).size())
22 typedef vector<int> VI;
23 typedef long long ll;
24 typedef pair<int, int> PII;
25 const ll MOD = 1e9 + 7;
26 const int INF = 0x3f3f3f3f;
27 const double EPS = 1e-10;
28 const double PI = acos(-1.0);
29 const int MAXN = 8010;
30 // head
31
32 const int MAX_V = MAXN;
33 struct edge { int to, cost; };
34 vector<edge> G[MAX_V];
35 int d[MAX_V];
36 int V;
37
38 void dijkstra() {
39     priority_queue<PII, vector<PII>, greater<PII> > que;
40     d[0] = INF;
41     que.push(PII(INF, 0));
42
43     while (!que.empty()) {
44         PII p = que.top(); que.pop();
45         int v = p.second;
46         if (d[v] > p.first) continue;
47         rep(i, 0, G[v].size()) {
48             edge e = G[v][i];
49             if (d[e.to] < min(d[v], e.cost)) {
50                 d[e.to] = min(d[v], e.cost);
51                 que.push(PII(d[e.to], e.to));
52             }
53         }
54     }
55 }
56
57 int n, m, k, w;
58 int a[MAXN], c[MAXN];
59
60 int main() {
61     scanf("%d%d%d%d", &n, &m, &k, &w);
62     V = n;
63     rep(i, 0, k) {
64         int t;
65         scanf("%d", &t);
66         a[--t] = 1;
67     }
68     while (m--) {
69         int x, y, z;
70         scanf("%d%d%d", &x, &y, &z);
71         x--, y--;
72         G[x].pb(edge{ y,z });
73         G[y].pb(edge{ x,z });
74     }
75     dijkstra();
76     rep(i, 0, n) if (a[i]) c[min(d[i] / w, k)]++;
77     int sum = 0, sur = 0, ans = k;
78     per(i, 1, k + 1) {
79         if (c[i]) sur += c[i] - 1;
80         else if (sur) sur--;
81         else ans--;
82     }
83     cout << ans << endl;
84     return 0;
85 }
时间: 2024-12-16 18:16:39

2017ACM省赛选拔赛题解的相关文章

第七届省赛赛前交流赛部分题解

A题: Yougth's Game[Ⅲ]( 区间dp ) 这是在省赛前热身赛出的题目,可能是题目中有用到博弈的思想,很多人都在做,而且在尝试暴力.但是没有人往dp的方向上想. 题目类型:动态规划+博弈 分析:题意描述的很清楚,就是选择在两端取数,当前取的数不仅能够影响下一次的结果,而且能够影响后面的结果...又是一个求最优值,那么是不是可以往dp的方向上想了.区间dp 定义状态dp[ i ] [ j ] 为从 i 到 j 上A的得分,那么B的得分就是sum(i,j)-dp[ i ] [ j ]

2016 SCNUCPC 校赛非官方题解

我要举报本次校赛出题人的消极出题!!! A. 树链剖分数据结构板题 B. The background of water problem 题目大意(大写加粗的水题):给定$N$个学生和他们$K$个科目的成绩$S_i$,再给出各科目$K_i$的权重顺序$Q_i$,求排名之后,拥有id为$X$的是哪个学生. 基本思路:虽然$K$只有$10$,$S$只有$100$,但有M组查询,所以当然不能开个long long去hash每个学生.我们简单点,开个结构体,排个序,就好了. 参考代码: 官方代码(将成绩

2017ACM省赛总结与生涯回顾

省赛本身的内容没有太多可说的,和去年一样和swt和ly这对情侣一队. 先说比赛相关的内容.热身赛A题不会,B题和C题都是水题,但是没有找出C的坑点,所以热身赛只做出了一题,赛后我也没有去解决,这次我纯怀打酱油的心理,没有怎么复习过.晚饭碰到聪神,安慰我说是给明天攒人品.晚上也按时睡觉了.正赛早上闹钟起来,挺困的,洗了头出门了.总之一切也比较平常.先看了三道比较水的题,我感觉我看的F比较水,先上去敲了,过一会裁判发来提示关于G的mod印刷问题,我以为只是一般的解释,没有在意直接关了,其实是他们正在

2015 ACM-ICPC 北京赛区 网络赛 部分 题解

由于博主水平及智商不足,所以暂时只能放出下列的题目的题解. A B D F G H 需要其他题解请自行前往 http://talk.icpc-camp.org/ 题目地址:hihocoder 1227-1236 A.  The Cats' Feeding Spots 题意:给出M个点,求以其中一个点为圆心,最小半径的圆,使得这个圆里恰好有N个点. (半径一定要是整数,且点不能恰好在圆上). 方法:暴力,稍微注意一下精度什么的就好了,1A. 1 #include <bits/stdc++.h>

2016 ICPC 大连网络赛 部分题解

先讲1007,有m个人,n种石头,将n种石头分给m个人,每两个人之间要么是朋友关系,要么是敌人关系,朋友的话他们必须有一种相同颜色的石头,敌人的话他们必须所有石头的颜色都不相同.另外,一个人可以不拥有任何一种石头.求m个人的所有关系是不是都能用n种石头表示出来.比赛当时找的关系是n种石头可以表示n+1个人的关系.但是一直WA,因为考虑不周. 我们考虑这样的一种情况,我们把人分为左边和右边两部分,每边的人里面都互相为敌人,同时左边的任意一个人和右边的任意一个人都是朋友.举个例子,左边有3人,右边两

NOIP模拟 (8-2情人节欢乐赛) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目来自hzwer的模拟题 果实计数 (count.pas/.c/.cpp) 时间限制:1s,空间限制32MB 题目描述: 淘淘家有棵奇怪的苹果树,这棵树共有n+1层,标号为0~n.这棵树第0层只有一个节点,为根节点.已知这棵树为b叉树,且保证是一颗满b叉树. 现在,该树第n层的每个节点上都结出了一个苹果,淘淘想知道共结了多少苹果.由于数量可能很大,答案要求输出mod k后的结果. 输入描述: 给出第1层的节点数b和层数n和k

哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练. 题解: E666 这个题是让求有多少个子串只含有6.寻找连续的6,然后用n*(n+1)/2求出这一段的子串个数,然后把每一段连续的加起来. 做的时候wa了很多次,原来是在n*(n+1)的地方已经超过int型了,所以需要设置类型为long long. #include <cstdio> #inc

郑轻第六届校赛 -- 部分题解

1427: 数字转换 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 379  Solved: 93 SubmitStatusWeb Board Description 老师交给小明一个任务,有两个数字x和y(x<y),通过以下两种操作:一.将x乘以2:二.将x的值加上1.小明希望能通过尽可能少的操作来完成这个任务,但是不知道怎么做,现在请大家来帮帮他的忙吧. Input 两个整数x,y(0<=x<y<=10^6). Output 一

【Contest】Nowcoder 假日团队赛1 题解+赛后总结

比赛链接 通过顺序:\(B\rightarrow D\rightarrow I\rightarrow J\rightarrow G\rightarrow H \rightarrow A \rightarrow K\rightarrow C\rightarrow E \rightarrow L \rightarrow F\) A 蹄球锦标赛: 给你\(N\)个点\(N\)条有向边,求在某些点开始跑边能够遍历整张图的最少点数 这已经是简化版的题意了,略去了前面没必要讲的建边(因为对于一个能看懂并写出