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();View Codeif(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> usingView Codenamespace 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; }View Code
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; }View Code
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])); }View Code