// 7.19-7.29 東北大學秦皇島校區十天訓練營,題目都掛在了Vjudge上。訓練期間比較忙,沒空更博總結,回來繼續補題消化。
Day1
這天授課主題是簡單圖論,節奏挺好,wls兩小時理完圖論里的基本知識點。
下午的賽題就偏入門了(簡單圖論無疑),只涉及到最短路問題和簡單的搜索以及一些奇怪的技巧。(差分約束呢?最小生成樹呢?強連通分量呢?)
A - Jzzhu and Cities (補)
把火車線路加上跑Dijkstra就好了,標記火車線路,相等時也要push。在最短路上的火車線路不能被取消,剩下的全部能取消。

#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<vector> using namespace std; typedef long long ll; const int maxn = 200010; struct Edge { int to; bool istrain; ll w; Edge(int v, bool is, ll ww):to(v), istrain(is), w(ww){} bool operator<(const Edge& a)const { if(w==a.w) return istrain; // 非火車節點先更新 return w > a.w; } }; vector<Edge> G[maxn]; bool vis[maxn]; int d[maxn]; int Dijkstra() { memset(d, 0x3f, sizeof(d)); memset(vis, 0, sizeof(vis)); d[1] = 0; int res = 0; priority_queue<Edge> q; q.push(Edge(1, 0, 0)); while(!q.empty()) { Edge tmp = q.top(); q.pop(); int u = tmp.to; if(vis[u]) continue; vis[u] = 1; // d[u] = tmp.w; if(tmp.istrain) ++res; for(int i=0;i<G[u].size();i++) { int v = G[u][i].to; if(!vis[v] && d[v]>=d[u]+G[u][i].w) { d[v] = d[u] + G[u][i].w; q.push(Edge(v, G[u][i].istrain, d[v])); } } } return res; } int main() { int n, m, k; cin>>n>>m>>k; int u, v, w; for(int i=0;i<m;i++) { scanf("%d %d %d", &u, &v, &w); G[u].push_back(Edge(v, 0, w)); G[v].push_back(Edge(u, 0, w)); } for(int i=0;i<k;i++) { scanf("%d %d", &v, &w); G[1].push_back(Edge(v, 1, w)); // G[v].push_back(Edge(1, 1, w)); } printf("%d\n", k-Dijkstra()); return 0; }
BFS 注意打標記!!!(雖然只是3*100的地圖也要爆內存!)

#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; int n, k, sx; char mp[3][110]; bool vis[3][110]; struct node { int x, y; node(int _x, int _y):x(_x), y(_y) {} }; bool check(int x, int y) { if(x<0 || x>2) return false; if(y>=n) return true; if(mp[x][y]=='.') return true; return false; } bool bfs() { queue<node> q; q.push(node(sx, 0)); while(q.size()) { node now = q.front(); q.pop(); if(now.y>=n) { return true; } // printf("(%d,%d) -> ", now.x, now.y); int nx = now.x, ny = now.y+1; if(!check(nx, ny)) continue; // 向右走一步 for(int i=-1;i<=1;i++) { // 嘗試三個方向移動 nx = now.x + i; if(check(nx, ny) && check(nx, ny+1) && check(nx, ny+2) && !vis[nx][ny+2]) { q.push(node(nx, ny+2)); vis[nx][ny+2] = 1; } } } return false; } int main() { int t; cin>>t; while(t--) { scanf("%d %d", &n, &k); getchar(); memset(vis, 0, sizeof(vis)); for(int i=0;i<3;i++) { scanf("%s", mp[i]); if(mp[i][0]=='s') sx = i; } printf("%s\n", bfs()?"YES":"NO"); } return 0; }
C - A Mist of Florescence (補)
構造題,技巧就是設計井字形的連通塊,把其他顏色塊塗到井字的格子上。

#include<iostream> #include<cstdio> using namespace std; int a, b, c, d; char ans[50][50]; void solve() { for(int i=1;i<=12;i++) { for(int j=1;j<50;j++) { if(i%2==1 && j%2==1 && a) ans[i][j] = 'A', --a; else ans[i][j] = 'D'; } } for(int i=13;i<=24;i++) { for(int j=1;j<50;j++) { if(i%2==1 && j%2==1 && b) ans[i][j] = 'B', --b; else ans[i][j] = 'D'; } } --c; --d; for(int i=25;i<=36;i++) { for(int j=1;j<50;j++) { if(i%2==1 && j%2==1 && c) ans[i][j] = 'C', --c; else ans[i][j] = 'D'; } } for(int j=1;j<50;j++) { ans[37][j] = 'C'; } for(int i=38;i<50;i++) { for(int j=1;j<50;j++) { if(i%2==1 && j%2==1 && d) ans[i][j] = 'D', --d; else ans[i][j] = 'C'; } } } int main() { cin>>a>>b>>c>>d; solve(); printf("49 49\n"); for(int i=1;i<50;i++) { for(int j=1;j<50;j++) printf("%c", ans[i][j]); printf("\n"); } return 0; }
DFS到牆的邊界 對每塊編號!

#include<iostream> #include<cstdio> using namespace std; int n, m, k; char mp[1010][1010]; int v[1010][1010], id; // v[i][j]: mp[i][j]的分類編號 id: 當前編號 int res[1010*1010]; // res[id]: 第id塊的答案 const int dx[] = {0, 0, 1, -1}; const int dy[] = {1, -1, 0, 0}; int ans; void dfs(int x, int y) { if(mp[x][y]=='*') { ans++; return; } v[x][y] = id; for(int i=0;i<4;i++) { int nx = x+dx[i], ny = y+dy[i]; if(nx>=0 && nx<m && ny>=0 && ny<n && !v[nx][ny]) { dfs(nx, ny); } } } int main() { scanf("%d %d %d", &m, &n, &k); getchar(); for(int i=0;i<m;i++) { scanf("%s", mp[i]); } for(int i=0;i<m;i++) { for(int j=0;j<n;j++) { if(mp[i][j]=='.' && !v[i][j]) { ++id; ans = 0; dfs(i, j); res[id] = ans; } } } while(k--) { int x, y; scanf("%d %d", &x, &y); printf("%d\n", res[v[x-1][y-1]]); } return 0; }
F - The Cild and Toy (補)
貪心!由於每去掉一個點,等價於去掉了所有與它相連的邊,就是問去掉全部邊的最小代價。答案當然就是每條邊兩個節點權值小的那頭的總和。都不用建圖!!

#include<iostream> #include<cstdio> using namespace std; const int maxn = 1010; int n, m; int w[maxn]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) { scanf("%d", &w[i]); } int u, v, ans = 0; for(int i=0;i<m;i++) { scanf("%d %d", &u, &v); ans += min(w[u], w[v]); } printf("%d\n", ans); return 0; }
對連通部分排序就完事了

#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; int n, num[310], id; int mp[310][310]; bool vis[310]; int ans[310]; struct list { vector<int> num; vector<int> id; }L[310]; void dfs(int x, int id) { vis[x] = 1; L[id].num.push_back(num[x]); L[id].id.push_back(x); for(int i=1;i<=n;i++) { if(mp[x][i]) { if(!vis[i]) dfs(i, id); } } } int main() { cin>>n; for(int i=1;i<=n;i++) { scanf("%d", &num[i]); } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { scanf("%1d", &mp[i][j]); } } for(int i=1;i<=n;i++) { if(!vis[i]) dfs(i, ++id); } for(int i=1;i<=id;i++) { // for(int j=0;j<L[i].num.size();j++) { // printf("%d:%d ", L[i].num[j], L[i].id[j]); // } // cout<<endl; sort(L[i].num.begin(), L[i].num.end()); sort(L[i].id.begin(), L[i].id.end()); for(int j=0;j<L[i].num.size();j++) { ans[L[i].id[j]] = L[i].num[j]; } } for(int i=1;i<=n;i++) { printf("%d%c", ans[i], i!=n?' ':'\n'); } return 0; }
H - Alyona and the Tree (補)
從根開始dfs就好,每個節點為max(0LL, now+w[i])

#include<iostream> #include<cstdio> #include<vector> #include<algorithm> using namespace std; const int maxn = 100010; typedef long long ll; struct Edge { int to; ll w; Edge(int v, ll ww):to(v), w(ww) {} }; vector<Edge> G[maxn]; int n, vw[maxn]; int ans; void dfs(int u, int fa, ll now) { for(int i=0;i<G[u].size();i++) { int v = G[u][i].to; if(v!=fa) { if(vw[v]>=now+G[u][i].w) { --ans; // printf("%d->%d\n", u, v); dfs(v, u, max(now+G[u][i].w, 0LL)); } } } } int main() { cin>>n; ans = n; for(int i=1;i<=n;i++) { scanf("%d", &vw[i]); } int u, w; for(int i=2;i<=n;i++) { scanf("%d %d", &u, &w); G[i].push_back(Edge(u, w)); G[u].push_back(Edge(i, w)); } dfs(1, -1, 0); printf("%d\n", ans-1); return 0; }
L - Love Triangle
假的三元環??

#include <iostream> #include <cstdio> #include <algorithm> #include <vector> using namespace std; const int maxn = 5010; int n, tot; int love[maxn], low[maxn]; bool vis[maxn]; bool ans; void dfs(int u) { if(ans) return; vis[u] = true; low[u] = ++tot; int v = love[u]; if(v && !vis[v]) { dfs(v); if(love[v] && low[love[v]]==low[u]+2 && love[love[v]]==u) { ans = true; return; } } } int main() { cin>>n; for(int i=1;i<=n;i++) { scanf("%d", &love[i]); } ans = false; for(int i=1;i<=n;i++) { if(!vis[i] && !ans) dfs(i); } printf("%s\n", ans?"YES":"NO"); return 0; }
並查集

#include <iostream> #include <cstdio> using namespace std; const int maxn = 500100; int n, m; int fa[maxn]; int g[maxn], cnt[maxn]; int Find(int x) { return fa[x]==x?x:(fa[x]=Find(fa[x])); } void Union(int x, int y) { int a = Find(x); int b = Find(y); if(a==b) return; fa[a] = b; } int main() { cin>>n>>m; for(int i=1;i<=n;i++) fa[i] = i; while(m--) { int k; scanf("%d", &k); for(int i=0;i<k;i++) { scanf("%d", &g[i]); } for(int i=1;i<k;i++) { Union(g[0], g[i]); } } for(int i=1;i<=n;i++) { cnt[Find(i)]++; } for(int i=1;i<=n;i++) { printf("%d%c", cnt[Find(i)], i!=n?' ':'\n'); } return 0; }
O - NP-Hard Problem (補)
二分圖裸題。。。

#include<iostream> #include<cstdio> #include<vector> using namespace std; typedef long long ll; const int maxn = 100010; int n, m; vector<int> G[maxn]; int deg[maxn]; int id[maxn]; vector<int> ans[2]; bool dfs(int u) { for(int i=0;i<G[u].size();i++) { int v = G[u][i]; if(!id[v]) { id[v] = 3 - id[u]; if(id[v]==1) ans[0].push_back(v); else ans[1].push_back(v); if(!dfs(v)) return false; } else if(id[v]==id[u]) return false; } return true; } int main() { cin>>n>>m; int u, v; for(int i=0;i<m;i++) { scanf("%d %d", &u, &v); G[u].push_back(v); G[v].push_back(u); ++deg[u]; } for(int i=1;i<=n;i++) { if(!deg[i]) continue; if(!id[i]) { id[i] = 1; ans[0].push_back(i); if(!dfs(i)) return 0 * printf("-1\n"); } } for(int k=0;k<=1;k++) { printf("%d\n", ans[k].size()); for(int i=0;i<ans[k].size();i++) { printf("%d%c", ans[k][i], i==ans[k].size()-1?'\n':' '); } } return 0; }
Day2
第二天的主題是簡單數論,下午掛的是CF上的一場區域賽:2015 ACM National Contest Romania
注意文件讀入要求!!!(第一題讓我WA了4次樣例。。。嘗試兩種方法寫)
子序列里和為偶數的個數,簡單dp(或組合數計算)

#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; const ll mod = 1e9+7; const int maxn = 1000100; int arr[maxn], n; ll a[maxn], b[maxn]; int main() { freopen("azerah.in", "r", stdin); freopen("azerah.out", "w", stdout); int t; cin>>t; while(t--) { scanf("%d", &n); for(int i=1;i<=n;i++) { scanf("%lld", &arr[i]); } a[1] = b[1] = 0; if(arr[1]%2==0) b[1] = 1; else a[1] = 1; for(int i=2;i<=n;i++) { if(arr[i]%2==0) { b[i] = (b[i-1]*2+1) % mod; a[i] = (a[i-1]*2)% mod; }else { b[i] = (a[i-1] + b[i-1]) %mod; a[i] = (a[i-1] + b[i-1] + 1) %mod; } // cout<<b[i]<<endl; } cout<<b[n]<<endl; } return 0; } /* #include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; const ll mod = 1e9+7; const int maxn = 1000100; int arr[maxn], n; ll pow(ll a, ll n) { ll res = 1; while(n) { if(n%2) { res = res * a %mod; } a = a*a % mod; n >>= 1; } return res; } int main() { freopen("azerah.in", "r", stdin); freopen("azerah.out", "w", stdout); int t; cin>>t; while(t--) { ll odd = 0, even = 0; scanf("%d", &n); for(int i=1;i<=n;i++) { scanf("%lld", &arr[i]); if(arr[i]%2==0) even++; else odd++; } if(odd==0) cout<<(pow(2, even)-1+mod)%mod<<endl; else cout<<(pow(2, odd-1)*pow(2, even)%mod-1+mod)%mod<<endl; } return 0; } */
B - Por Costel and the Algorithm (補)
給了一段Bellman-Ford代碼,調整邊的順序讓它最多執行兩次。
記錄最短路上的邊即可,dfs輸出邊的順序。

#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<vector> using namespace std; typedef long long ll; const int maxn = 100100; int n, m; struct Edge{ int to; int id; ll w; Edge(int v, int i, ll _w):to(v), id(i), w(_w) {} bool operator<(const Edge& e)const { return w > e.w; } }; vector<Edge> G[maxn]; ll d[maxn]; int e[maxn]; // e[v]: 連向v的邊的編號 bool path[2*maxn]; void Dijkstra() { memset(d, 0x3f, sizeof(d)); d[1] = 0; priority_queue<Edge> q; q.push(Edge(1, 0, 0)); while(!q.empty()) { Edge tmp = q.top(); q.pop(); int u = tmp.to; for(int i=0;i<G[u].size();i++) { int v = G[u][i].to; if(d[v]>d[u]+G[u][i].w) { d[v] = d[u] + G[u][i].w; path[e[v]] = 0; path[G[u][i].id] = 1; e[v] = G[u][i].id; q.push(Edge(v, 0, d[v])); } } } } void dfs(int u) { for(int i=0;i<G[u].size();i++) { int m = G[u][i].id, v = G[u][i].to; //printf("%d->%d %d", u, v, m); if(path[m]) { printf("%d ", m); dfs(v); } } } int main() { freopen("algoritm.in", "r", stdin); freopen("algoritm.out", "w", stdout); int t; cin>>t; while(t--) { memset(e, 0, sizeof(e)); memset(path, 0, sizeof(path)); for(int i=0;i<maxn;i++) G[i].clear(); scanf("%d %d", &n, &m); for(int i=1;i<=m;i++) { int u, v; ll w; scanf("%d %d %lld", &u, &v, &w); G[u].push_back(Edge(v, i, w)); } Dijkstra(); dfs(1); for(int i=1;i<=m;i++) { if(!path[i]) printf("%d ", i); } printf("\n"); } return 0; }
D - Por Costel and the Censorship Committee
F - Por Costel and the Alien Invasion
G - Por Costel and the Orchard
並查集裸題(可以用帶權並查集做?)

#include<iostream> #include<cstdio> using namespace std; const int maxn = 100010; int n, m; int fa[maxn*2]; int Find(int x) { return fa[x]==x?x:(fa[x]=Find(fa[x])); } void Union(int x, int y) { int a = Find(x), b= Find(y); if(a!=b) fa[a] = b; } int main() { freopen("meciul.in", "r", stdin); freopen("meciul.out", "w", stdout); int t; cin>>t; while(t--) { scanf("%d %d", &n, &m); for(int i=1;i<=2*n;i++) fa[i] = i; int x, y; while(m--) { scanf("%d %d", &x, &y); if(Find(x)!=Find(y) && Find(x+n)!=Find(y+n)) { printf("YES\n"); Union(x, y+n); Union(x+n, y); } else { printf("NO\n"); } } } return 0; }
求[n/i],分塊(也可以只計算k=sqrt(n)的結果,ans*2 - k*k就是最終答案)
ll ans = 0;
for(int i=1;i<=n;) {
ans += n/i * (n/(n/i) - i +1);
i = n/(n/i) + 1;
}
K - Por Costel and the Firecracker (補)
沒有內存限制的話就是水題,可是完全沒有優化空間。。。
百度之,學會了分塊打表
把第一次查詢看成了x1,debug了半天。。。

#include<iostream> #include<cstdio> using namespace std; typedef long long ll; const int maxn = 10000010; const int mod = 10000003; ll n, a, b, x1, q, q1; ll ans[maxn/100+10]; void pre() { ans[0] = x1; ll last = x1; for(ll i=1;i<maxn;i++) { last = (last*i % n + a) % n; if(i%100==0) { ans[i/100] = last; } // cout<<i<<' '<<ans[i]<<endl; } } ll cal(ll k) { if(k%100==0) return ans[k/100]; ll last = ans[k/100]; for(ll i=k/100*100+1;i<=k;i++) { last = (last*i % n + a) % n; } return last; } int main() { freopen("pocnitoare.in", "r", stdin); freopen("pocnitoare.out", "w", stdout); cin>>n>>a>>b>>x1>>q>>q1; pre(); printf("%lld\n", x1=cal(q1-1)); // 第一次查詢是q1-1而不是x1-1!!! for(int i=1;i<q;i++) { q1 = (i*x1 % mod + b) % mod + 1; printf("%lld\n", x1=cal(q1-1)); } return 0; }
L - Por Costel and the Semipalindromes
WA了半天,原來是1左移出現了溢出,ll范圍一定要寫成 (1LL<<k)

#include<iostream> #include<cstdio> using namespace std; typedef long long ll; char res[1010]; int tmp[1010]; int main() { freopen("semipal.in", "r", stdin); freopen("semipal.out", "w", stdout); int t; cin>>t; while(t--) { int n; ll k; scanf("%d %lld", &n, &k); res[1] = res[n] = 'a'; if(k>(1LL<<(n-2))) { k -= (1LL<<(n-2)); res[1] = res[n] = 'b'; } --k; for(int i=0;i<n-2;i++) { tmp[n-1-i] = (k>>i)&1; } for(int j=2;j<n;j++) { if(tmp[j]) { res[j] = 'b'; } else { res[j] = 'a'; } } for(int i=1;i<=n;i++) { putchar(res[i]); } puts(""); } return 0; }
Day3
題目來自於NAIPC 2016北美邀請賽。
E - K-Inversions
給定一個只含有AB的字符串(長度不超過1000000),求間隔為1~n-1的B-A有多少對。
FFT模板題
F - Mountain Scenes
給長度總和為n的木條以及相框的寬度w和高度h,問能組成多少種不同的山峰形狀。
大家的簽到dp題,我的自閉題。。。 j 沒有從0開始樣例死都調不出來 噗
也就是dp[i][0] = 1
dp[i][j] 為寬度 i 用了總長 j 的木條的方案數。 dp[i+1][j] = sum( dp[i][j-k] ) ( 0<=k<=h, j )

#include<iostream> #include<cstdio> using namespace std; typedef long long ll; const int maxn = 10010; const int mod = 1e9+7; int n, w, h; ll dp[110][maxn]; int main() { cin>>n>>w>>h; // for(int i=0;i<=h && i<=w;i++) dp[i][0] = 1; dp[0][0] = 1; for(int i=1;i<=w;i++) { for(int j=0;j<=n;j++) { // j=0 開始。。。坑死自己了 for(int k=0;k<=h && k<=j;k++) { dp[i][j] = (dp[i][j] + dp[i-1][j-k]) % mod; } } } ll ans = 0; for(int i=1;i<=n;i++) ans = (ans + dp[w][i])%mod; cout<<(ans-min(n/w, h))%mod<<endl; return 0; }
G - Symmetry
平面上有 n 個點( n<=1000),問至少加入多少點,使全部點與某一個點中心對稱,或者全部點關於某條過其中兩點的直線對稱。
計算幾何。
對於要求1,枚舉全部點再check剩下點是否中心對稱,復雜度O(n^2logn),可行。
對於要求2,枚舉兩點所得直線再check剩下點是否關於直線對稱,復雜度O(n^3logn),TLE。
看題解好像是把直線去重才能過。
I - Tourists
求樹上所有整除關系的兩對節點的最短路之和。
計算所有符合條件的節點復雜度O(nlogn),樹上兩點 u, v 的最短路為 dep[u] + dep[v] - 2*dep[lca(u, v)] + 1。
找了個LCA的板子就1A了 O.O (第四個板子是改進的樹鏈剖分求LCA,復雜度O(n + mlogn),是其中最快的。)

#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 500010; struct Edge { int to, next; }edges[maxn]; int head[maxn], tot; int fa[maxn], siz[maxn], dep[maxn], top[maxn]; // top[u] == u : u不是重兒子 // top[u] == fa: fa的重兒子是u void add(int u, int v) { edges[++tot].to = v; edges[tot].next = head[u]; head[u] = tot; } void dfs(int u) { // 求fa, siz, dep, top int maxSon = 0, son = 0; top[u] = u; siz[u] = 1; for(int i=head[u];i;i=edges[i].next) { int v = edges[i].to; if(v==fa[u]) continue; fa[v] = u; dep[v] = dep[u] + 1; dfs(v); siz[u] += siz[v]; if(siz[v]>maxSon) maxSon = siz[son=v]; } if(son) // 重兒子 top[son] = u; } int Find(int u) { return u==top[u]?u:top[u]=Find(top[u]); } int LCA(int u, int v) { if(Find(u)!=Find(v)) return dep[top[u]]<dep[top[v]] ? LCA(u, fa[top[v]]):LCA(v, fa[top[u]]); else return dep[u]<dep[v] ? u:v; } int main() { int n; cin>>n; for(int i=0;i<n-1;i++) { int u, v; scanf("%d %d", &u, &v); add(u, v); add(v, u); } memset(fa, -1, sizeof(fa)); dfs(1); long long ans = 0; for(int i=1;i<=n/2;i++) { for(int j=i*2;j<=n;j+=i) { ans += dep[i]+dep[j]-2*dep[LCA(i, j)] + 1; } } printf("%lld\n", ans); return 0; }
Day4
A - One-dimensional Japanese Crossword
簡單模擬題。
B - Passwords
簡單數學題。
C - Journey
簡單模擬題。

#include<iostream> #include<cstdio> using namespace std; bool check(int h, int m) { // cout<<h<<":"<<m<<endl; if(h%10==7 || m%10==7) return true; return false; } int main() { int x, h, min; scanf("%d", &x); scanf("%d %d", &h, &min); int y = 0; while(!check(h, min)) { if(x==60) { if(--h<0) h = 23; } else { min -= x; if(min<0) { min += 60; if(--h<0) h = 23; } } ++y; } printf("%d\n", y); return 0; }
F - Jamie and Binary Sequence (changed after round)
給定 n 和 k,將 n 分解成 k 個二次冪之和,求2的指數最小且字典序最大的一組解,不存在則輸出 No。
思維題。
把 n 轉成二進制,要保證最高次冪最小,當二進制位1個數小於k時,把最高位2^n拆成 2^(n-1) + 2^(n-1),這樣多了一個1。
當拆分之后1的數量大於k時,要保證字典序最大,再從最低位開始拆分,每次只拆一個1,新的低位加2。

#include<iostream> #include<cstdio> #include<algorithm> using namespace std; typedef long long ll; ll n; int k, bits[100100], cnt, high, len; int tmp[65]; int main() { cin>>n>>k; ll nn = n; for(int i=0;nn;i++) { tmp[i] = nn&1; nn >>= 1; len = i; if(tmp[i]) ++cnt; } for(int i=len;i>=0;i--) { bits[len-i] = tmp[i]; } high = len; // 記錄最高位指數 // for(int i=0;i<=len;i++) cout<<bits[i]; if(cnt>k) return 0 * printf("No\n"); int pos = 0; while(cnt<k) { if(cnt+bits[pos]<=k) { // 最小化y=max(ai) cnt += bits[pos]; bits[pos+1] += 2*bits[pos]; bits[pos] = 0; ++pos; len = max(pos, len); // 忘記更新,WA66組 } else { pos = len; while(!bits[pos]) --pos; while(cnt<k) { ++cnt; bits[pos] -= 1; bits[++pos] += 2; } } } printf("Yes\n"); int up = max(pos, len); for(int i=0;i<=up;i++) { for(int j=0;j<bits[i];j++) printf("%d ", high-i); } return 0; }
G - Jamie and Interesting Graph
需要構造出一個n個節點m條邊的圖,滿足最小生成樹的權為素數,1到n的最短路為素數。
構造1~n的一條鏈為最小生成樹,權為p = 100019。剩下不停加邊,邊權為2p。

#include<iostream> #include<cstdio> using namespace std; int p = 100019; int main() { int n, m; cin>>n>>m; printf("%d %d\n", p, p); printf("1 2 %d\n", p-(n-2)); for(int i=2;i<n;i++) printf("%d %d 1\n", i, i+1); m -= n-1; for(int i=1;i<n && m;i++) for(int j=i+2;j<=n && m;j++) { printf("%d %d %d\n", i, j, 2*p); --m; } return 0; }
H - 樹上染色
很經典的樹形dp。
// f[u][j]: 以u為根節點,塗了 j 個黑點的子樹對答案的最大貢獻
對於一條邊,左邊子樹有 x 個黑點,則右邊有 k-x 個黑點,左邊白點為 size[v] - x,右邊白點為 n - size[v] - (k-x),按照題意計算這部分貢獻 val。
狀態轉移方程為 f[u][j] = max( f[u][j], f[u][j-x] + f[v][x] + val),j 倒序枚舉更新。

#include<iostream> #include<cstdio> #include<vector> #include<cstring> #define max(x, y) ((x) > (y) ? (x) : (y)) #define min(x, y) ((x) < (y) ? (x) : (y)) using namespace std; typedef long long ll; const int maxn = 2010; struct Edge { int u, w; Edge(int uu, int ww):u(uu), w(ww) {} }; vector<Edge> G[maxn]; int siz[maxn], n, kk; ll f[maxn][maxn]; // f[u][j]: 節點i含有j個黑點的子樹對答案的最大貢獻 void dfs(int u, int fa) { f[u][0] = f[u][1] = 0; siz[u] = 1; for(int i=0;i<G[u].size();i++) { int v = G[u][i].u; if(v==fa) continue; dfs(v, u); siz[u] += siz[v]; } for(int i=0;i<G[u].size();i++) { int v = G[u][i].u, w = G[u][i].w; if(v==fa) continue; for(int j=min(kk, siz[u]);j>=0;j--) { for(int k=0;k<=min(j, siz[v]);k++) { if(f[u][j-k]!=-1) { ll val = (1LL*k*(kk-k)+ 1LL*(siz[v]-k)*(n-kk-(siz[v]-k)))*w; f[u][j] = max(f[u][j], f[u][j-k]+f[v][k]+val); } } } } } int main() { scanf("%d %d", &n, &kk); for(int i=1;i<n;i++) { int u, v, w; scanf("%d %d %d", &u, &v, &w); G[u].push_back(Edge(v, w)); G[v].push_back(Edge(u, w)); } memset(f, -1, sizeof(f)); dfs(1, -1); printf("%lld\n", f[1][kk]); return 0; }
Day5
Day6