Comet OJ - Contest #8 解题报告

房东的猫 提交于 2019-11-27 08:35:56

传送门:https://www.cometoj.com/contest/58


A:杀手皇后(STL)

题意:给出n个由小写字母组成的字符串,求出字典序最小的那一个。

分析:签到题,用string类的重载运算符 < 可以比较两个字符串的字典序大小。维护一个字典序最小的字符串变量即可得到答案。

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 int main() {
 6     int n;
 7     scanf("%d",&n);
 8     string ans = "";
 9     ans += char(200);
10     for (int i=0;i<n;i++) {
11         string input;
12         cin >> input;
13         if (input < ans) {
14             ans = input;
15         }
16     }
17 
18     cout << ans << endl;
19 
20     return 0;
21 }
View Code

B:支援城市(数学)

题意:有n个点,其值为w。求对于每个点x,求$\sum_{i=1}^{n}$(wi+wx)2

分析:条件很少,暴力的话O(n2)的复杂度会超时,所以剩下能做的事情就是因式分解。通过分析分解后的式子我们可以得到对于每个点x,其答案都会等于n*wx2 + $\sum_{i=1}^{n}$wi2 + 2*wx$\sum_{i=1}^{n}$wi,因此用代码把公式表达一遍即可。

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 int main() {
 6     int n;
 7     scanf("%d",&n);
 8 
 9     long long x[n];
10     long long sum = 0,sum2 = 0,ans = 0;
11     for (int i=0;i<n;i++) {
12         scanf("%lld",&x[i]);
13         sum += x[i];
14         sum2 += x[i] * x[i];
15     }
16 
17     for (int i=0;i<n;i++) {
18         ans = (n)*x[i]*x[i] + sum2 - 2*x[i]*sum;
19         if (i)
20             printf(" ");
21         printf("%lld",ans);
22     }
23     printf("\n");
24 
25     return 0;
26 }
View Code

 C:符文能量(DP)

题意:有n块石头,每块石头可用二元组P(a,b)表示。你可以选择任意两块相邻的石头Pi和Pi+1合并,得到新的石头(ai,bi+1)并释放能量 ai+1 * bi。你可以以任意顺序合并石头,在合并前,你还可以选择一段区间的石头进行精炼,使得这段区间里的二元组乘k变为(a*k,b*k)。求将所有的石头合并成一块最少需要释放多少能量。

分析:(1)首先,针对每次合并每次操作,两块石头都是相邻的,合并后的新二元组是(前一块石头的a,后一块石头的b),可以注意到,无论以何种顺序合并,都不会影响总释放能量,因此这道题的重点就在于选哪段区间进行精炼可以使得总释放能量最小。

   (2)因为区间只可选一次且须是连续的,考虑用DP解决问题,我们从三个维度设计状态来为状态转移做准备,设DP[i][0]表示从区间[0,i]没有进行精炼所产生的能量值、dp[i][1]表示精炼区间包含了i时所能产生的最小能量值、dp[i][2]表示精炼区间存在但不包含i所能产生的最小能量值。

   (3)状态转移:dp[i][0] = dp[i-1][0] + (bi-1 * ai)(上一个状态的能力值加上当前状态产生的能量值)

            dp[i][1] = min(dp[i-1][0] + (bi-1 * ai*k), dp[i-1][1] + (bi-1*k * ai*k))(区间包含i,这个点可能是精炼区间的头部,也有可能是已有区间的尾部)

            dp[i][2] = min(dp[i-1][1] + (bi-1*k * ai), dp[i-1][2] + (bi-1 * ai))(区间不包含i,尾部可以在i-1,也可以在i-1之前)

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 int main() {
 6     int n,k;
 7     scanf("%d%d",&n,&k);
 8 
 9     int a[n],b[n];
10     for (int i=0;i<n;i++) {
11         scanf("%d%d",&a[i],&b[i]);
12     }
13 
14     long long dp[n][3];
15     memset(dp,0,sizeof(dp));
16     for (int i=1;i<n;i++) {
17         //从0到i没有精炼过
18         dp[i][0] = dp[i-1][0] + b[i-1]*a[i];
19         //精炼区间包含i时所产生的最小值
20         dp[i][1] = min(dp[i-1][0]+b[i-1]*a[i]*k,dp[i-1][1] + b[i-1]*a[i]*k*k);
21         //精炼区间不包含i时所产生的最小值
22         dp[i][2] = min(dp[i-1][1]+b[i-1]*a[i]*k,dp[i-1][2]+b[i-1]*a[i]);
23     }
24 
25     printf("%lld\n",min(min(dp[n-1][0],dp[n-1][1]),dp[n-1][2]));
26 
27     return 0;
28 }
View Code

D:菜菜种菜(树状数组)

题意:有 n 块土地,每块土地有一个菜值。它们之间有 m 条小路,每条连接两块土地,小路只能单向通行,不存在一条小路两端连接同一块土地,但可能存在两条小路两端连接的土地分别相同。如果存在一条从土地 u 到土地 v 的小路,则称 u 能直接到达 v。

菜菜可以购买一些土地,他必须在其中选择一块建造自己的家,所购买的剩下的土地都被作为菜地。因为菜菜不想种菜,所以他希望从他家能直接到达的土地中,一块菜地也没有(如果菜菜家不能直接到达任何一块土地,这也能满足他的愿望)。

菜菜提出了 q 个问题,每个问题给出 L,R ,询问如果他购买了第 L 到第 R 块之间的所有土地,那么所有满足他愿望的建造家的选址的菜值之和是多少?

分析:(1)首先,我们选的是一块区间,判断区间内的这个点可否作为家的依据,只看这个点的安全区间[l,r]是否大于我们所选的区间。这里的安全区间指的是一个点能直接到达的土地中,在它左边最靠右的一块(记为l,不存在则为 0)和在它右面最靠左的一块(记为r,不存在则为n + 1)所构成的区间;换句话说在这个区间(l,r)范围内购置土地,该点一定不会到达购置区间内的任意点。我们可以在输入小路时就顺便把这些点的安全区间给预处理出来了。

   (2)由上可得,我们的问题转化为在所选区间[L,R]内,有多少个点的安全区间大于[L,R],即l <= L , r >= R,这些点的点值之和便是我们要的答案了。我们将数据稍做处理,再离线处理询问,便可用树状数组解决该问题。

  1 #include <bits/stdc++.h>
  2 
  3 using namespace std;
  4 
  5 const int MAXN = 1e6+10;
  6 
  7 struct Interval {
  8     int l,r,id;
  9 
 10     bool operator < (const Interval &p) const {
 11         return r < p.r;
 12     }
 13 };
 14 
 15 struct Node {
 16     int l,r,x;
 17 };
 18 
 19 struct BinIdxTree {
 20     int node[MAXN];
 21     int n;
 22 
 23     void init(int x) {
 24         n = x;
 25         for (int i=0;i<=n;i++) {
 26             node[i] = 0;
 27         }
 28     }
 29 
 30     int lowbit(int x) {
 31         return x & (-x);
 32     }
 33 
 34     void update(int i,int val) {
 35         while (i <= n) {
 36             node[i] += val;
 37             i += lowbit(i);
 38         }
 39     }
 40 
 41     int sum(int i) {
 42         int ans = 0;
 43         while (i > 0) {
 44             ans += node[i];
 45             i -= lowbit(i);
 46         }
 47         return ans;
 48     }
 49 
 50 } BIT;
 51 
 52 int main() {
 53     int n,m,q;
 54     scanf("%d%d%d",&n,&m,&q);
 55     BIT.init(n);
 56 
 57     int l[n+1],r[n+1],a[n+1];
 58     for (int i=1;i<=n;i++) {
 59         scanf("%d",&a[i]);
 60         l[i] = 0;
 61         r[i] = n+1;
 62     }
 63 
 64     for (int i=0;i<m;i++) {
 65         int u,v;
 66         scanf("%d%d",&u,&v);
 67         if (v > u) {
 68             r[u] = min(r[u],v);
 69         } else {
 70             l[u] = max(l[u],v);
 71         }
 72     }
 73 
 74     Interval query[q];
 75     for (int i=0;i<q;i++) {
 76         scanf("%d%d",&query[i].l,&query[i].r);
 77         query[i].id = i;
 78     }
 79 
 80     vector<Node> V[n+2];
 81     for (int i=1;i<=n;i++) {
 82         V[i].push_back(Node{l[i]+1,i,a[i]});
 83         V[r[i]].push_back(Node{l[i]+1,i,-a[i]});
 84     }
 85 
 86     sort(query,query+q);
 87     int L = 1;
 88     int ans[q];
 89     for (int i=0;i<q;i++) {
 90         while (L <= query[i].r) {
 91             for (auto &it : V[L]) {
 92                 BIT.update(it.l,it.x);
 93                 BIT.update(it.r+1,-it.x);
 94             }
 95             L++;
 96         }
 97         ans[query[i].id] = BIT.sum(query[i].l); 
 98     }
 99 
100     long long result = 0;
101     for (int i=0;i<q;i++) {
102         result ^= (long long)(i+1)*ans[i];
103     }
104 
105     printf("%lld\n",result);
106 
107     return 0;
108 }
View Code
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!