COCI2014-2015 Contest#1 题目选做
COCI2014-2015 Contest#1 题目选做
A.PROSJEK
Description
有一个数列 a,现在按照下列公式求出一个数列 $ b_i = \frac{{}\sum_{j = 1}^{i}a_i}{i} $
给你数列 b,请求出数列 a。
Solution
显然通过b预处理出前缀和直接搞定
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int MAXN = 100 + 10; long long sum[MAXN]; int main() { int n = read(); for(int i=1;i<=n;i++) { int x = read(); sum[i] = 1LL * x * i; } for(int i=1;i<=n;i++) printf("%lld ",sum[i] - sum[i-1]); }
B.KLOPKA
Description
在平面直角坐标系上有 n 个点。
现在要用一个正方形将点框起来,使得每一个点都能在正方形的内部或边上。要求这个正方形的边平行于坐标轴。
求出这个正方形的最小面积。
Solution
记录一下横纵坐标最大最小值即可
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int MAXN = 20 + 10; int n; int x[MAXN],y[MAXN]; int maxx,maxy,minx,miny; int main() { n = read(); minx = 1<<30,miny = 1<<30; for(int i=1;i<=n;i++) { x[i] = read(),y[i] = read(); maxx = max(maxx,x[i]); maxy = max(maxy,y[i]); minx = min(minx,x[i]); miny = min(miny,y[i]); } cout << max((maxx - minx)*(maxx - minx),(maxy - miny) * (maxy - miny)) << endl; }
D.MAFIJA
Description
有 n 个人,其中有一些人是平民,有一些人是坏蛋。
现在,平民们想揪出所有的坏蛋,于是 n 个人都指认了一个人是坏蛋。
如果一个人是平民,他会随便乱指认,否则,他会指认一个平民。
求出最多的坏蛋个数。
Solution
把指认的关系当做边连边,把坏蛋当做染成1,好人当做染成0
由于坏蛋不能连坏蛋,与1相连的点一定为0
假如是一条链的话,显然交叉染色,从链尾开始染更优
把链扩展成树,发现每次把入度为0的点染成1一定最优
但是实际上有可能出现环
考虑为环的情况,如果环上的点都没有限制,显然随便选一个点染成0,这个点两边染色就没有限制了,就断成链了
考虑环上的点有限制的情况,即为基环树的情况
还是从入度为0的点开始染,环上有点被确定染成了0那么这个环显然就被断成链了,就按找链的方式贪心即可
所以可以设计出这样一个贪心算法
每次把入度为0且没有确定下来的点染成1,这样最后剩下的一定是几个独立的环
每次随便选环上一点染成0,把环断成链即可
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int MAXN = 5e5 + 10; int n; int a[MAXN]; int deg[MAXN]; bool vis[MAXN]; int ans; inline void dfs(int x,int col) { if(vis[x]) return; vis[x] = 1; ans += col; deg[a[x]]--; if((!deg[a[x]])||col == 1) dfs(a[x],col^1); } int main() { n = read(); for(int i=1;i<=n;i++) a[i] = read(),deg[a[i]]++; for(int i=1;i<=n;i++) if(!deg[i]) dfs(i,1); for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0); cout << ans << endl; }
E.ZABAVA
Description
一座新的公寓开放了,这座公寓有 m 栋楼。
现在会有 n 个学生,每一天都会进来一个人。
一栋楼进来一个人后,会进行一场派对,派对会有与当前人数相等的吵闹指数。
但是,现在可以进行 k 次操作,每一次操作可以将一栋楼里的全部学生踢出这座新公寓。
请注意,学生先进楼,然后才能进行操作。
现在求出最小吵闹指数的相加之和。
你不必使用完全部的操作。
Solution
注意到每座公寓是独立的
每个公寓对答案的贡献只和分了几次有关
这就是背包问题
考虑如何计算每个公寓分X次的最小吵闹和
容易发现人数是一定,要最小化答案一定是尽量均分
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int MAXN = 500 + 10; long long n,m,k; long long p[MAXN]; long long f[MAXN][MAXN]; inline long long calc(long long x,long long K) { long long val = x/K; long long num1 = (val+1) * K - x; long long num2 = K - num1; return num2 * (val + 1) * (val + 2)/2 + num1 * val * (val + 1)/2; } int main() { n = read(),m = read(),k = read(); for(int i=1;i<=n;i++) { int x = read(); p[x]++; } memset(f,0x3f,sizeof(f)); for(int j=0;j<=k;j++) f[0][j] = 0; for(int i=1;i<=m;i++) { for(int j=0;j<=k;j++) { for(int h=0;h<=j;h++) { int l = j - (h); f[i][j] = min(f[i][j],f[i-1][h] + calc(p[i],l+1)); } } } cout << f[m][k] << endl; }
F.Kamp
Description
一颗树 n 个点,n−1 条边,经过每条边都要花费一定的时间,任意两个点都是联通的。
有 K 个人(分布在 K 个不同的点)要集中到一个点举行聚会。
聚会结束后需要一辆车从举行聚会的这点出发,把这 K 个人分别送回去。
请你回答,对于 i=1∼n,如果在第 i个点举行聚会,司机最少需要多少时间把 K 个人都送回家
Solution
很简单的换根DP
考虑到以X为根的答案,相当于X的子树中的答案,以及它到子树外的答案减掉可以省下的最大距离
就维护一下就没了
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int MAXN = 500000 + 10; int n,k; vector<pair<int,int> >G[MAXN]; int a[MAXN],sz[MAXN]; long long f[MAXN],g[MAXN],h[MAXN]; long long dis1[MAXN],dis2[MAXN]; inline void dfs(int x,int ff) { sz[x] += a[x]; for(int i=0;i<G[x].size();i++) { int v = G[x][i].first; if(v == ff) continue; dfs(v,x); sz[x] += sz[v]; if(sz[v]) { f[x] += (f[v] + 2 * (G[x][i].second)); long long w = G[x][i].second; if(dis1[x] < dis1[v] + w) { dis2[x] = dis1[x]; dis1[x] = dis1[v] + w; } else if(dis2[x] < dis1[v] + w) dis2[x] = dis1[v] + w; } } } inline void dp(int x,int ff) { for(int i=0;i<G[x].size();i++) { int v = G[x][i].first; if(v == ff) continue; if(sz[v] == k) { dp(v,x); continue; } g[v] = g[x] + (f[x] - f[v]); long long w = G[x][i].second; if(!sz[v]) g[v] += 2 * ( G[x][i].second); if(dis1[v]+w == dis1[x]) h[v] = max(h[x] , dis2[x]) + w; else h[v] = max(h[x],dis1[x]) + w; dp(v,x); } } int main() { n = read(),k = read(); for(int i=1;i<n;i++) { int a = read(),b = read(),c = read(); G[a].push_back(make_pair(b,c)); G[b].push_back(make_pair(a,c)); } for(int i=1;i<=k;i++) a[read()]++; dfs(1,0); //cout << 1 << endl; dp(1,0); for(int i=1;i<=n;i++) printf("%lld\n",f[i] + g[i] - max(dis1[i] , h[i])); }