AtCoder Beginner Contest 209 題解
阿新 • • 發佈:2021-07-12
本場連結:AtCoder Beginner Contest 209
C - Not Equal
不難注意到:\(A_i\)的次序無關,因為每個元素都不同,只需要考慮每個元素在他的區間內的取值即可.因此按上升對\(C_i\)排序,由於整個陣列成上升,所以當做到\(C_i\)的時候,上限相當於去掉了\(i - 1\)個元素,如此即可統計答案.
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define forn(i,x,n) for(int i = x;i <= n;++i) #define forr(i,x,n) for(int i = n;i >= x;--i) #define Angel_Dust ios::sync_with_stdio(0);cin.tie(0) const int N = 2e5+7,MOD = 1e9 + 7; int c[N]; int main() { int n;scanf("%d",&n); forn(i,1,n) scanf("%d",&c[i]); sort(c + 1,c + n + 1); int res = 1; forn(i,1,n) res = 1ll * res * max(0,c[i] - i + 1) % MOD; printf("%d\n",res); return 0; }
D - Collision
弱智題,兩個點會走到一個點是距離為偶數,反之是奇數.求樹上任意兩點距離即可.
#define _CRT_SECURE_NO_WARNINGS #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5+7,M = 4e5+7,LIM = 17; int edge[M],succ[M],cost[M],ver[N],idx; int n,m,hh,tt,q[M]; int depth[N],fa[N][LIM + 3],dist[N]; void add(int u,int v,int w) { edge[idx] = v; cost[idx] = w; succ[idx] = ver[u]; ver[u] = idx++; } void bfs() { memset(depth,0x3f,sizeof depth); memset(dist,0x3f,sizeof dist); depth[1] = 0;depth[0] = -1;q[++tt] = 1;dist[1] = 0; while(hh <= tt) { int u = q[hh++]; for(int i = ver[u];~i;i = succ[i]) { int v = edge[i]; if(depth[v] > depth[u] + 1) { dist[v] = dist[u] + cost[i]; depth[v] = depth[u] + 1; fa[v][0] = u; q[++tt] = v; for(int k = 1;k <= LIM;++k) fa[v][k] = fa[fa[v][k - 1]][k - 1]; } } } } int lca(int x,int y) { if(depth[x] < depth[y]) swap(x,y); for(int i = LIM;i >= 0;--i) if(depth[fa[x][i]] >= depth[y]) x = fa[x][i]; if(x == y) return x; for(int i = LIM;i >= 0;--i) if(fa[x][i] != fa[y][i]) x = fa[x][i],y = fa[y][i]; return fa[x][0]; } int main() { memset(ver,-1,sizeof ver); scanf("%d%d",&n,&m); for(int i = 1;i < n;++i) { int u,v;scanf("%d%d",&u,&v); add(u,v,1);add(v,u,1); } bfs(); while(m--) { int u,v;scanf("%d%d",&u,&v); if((dist[u] + dist[v] - 2 * dist[lca(u,v)]) % 2 == 0) puts("Town"); else puts("Road"); } return 0; }
E - Shiritori
首先優化建圖:如果直接建圖整個圖的邊數會達到\(N^2\)無法承受.將所有首尾三個元素相同的元素合併到一個點,邊由每個\(s_i\)決定:首對應的點是\(u\)尾對應的點是\(v\)則建\(u->v\)的邊.
考慮求答案:如果這張圖上沒有環顯然是可以直接遞推的:在反圖上跑拓撲排序即可.但是有環其實也不影響,最終所有沒有確定狀態的點都是平局.
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pii; #define forn(i,x,n) for(int i = x;i <= n;++i) #define forr(i,x,n) for(int i = n;i >= x;--i) #define Angel_Dust ios::sync_with_stdio(0);cin.tie(0) #define x first #define y second const int N = 52 * 52 * 52 + 7,M = 2 * N,C = 2e5+7; char s[10]; vector<pii> edges(C); vector<int> E[N]; int deg[N],ans[N]; inline int gpos(char a) { if(a >= 'A' && a <= 'Z') return a - 'A'; return a - 'a' + 26; } inline int gid(char a,char b,char c) { return gpos(a) * 52 * 52 + gpos(b) * 52 + gpos(c); } int main() { int n;scanf("%d",&n); forn(i,1,n) { scanf("%s",s + 1);int len = strlen(s + 1); edges[i] = {gid(s[1],s[2],s[3]),gid(s[len - 2],s[len - 1],s[len])}; ++deg[edges[i].x]; E[edges[i].y].push_back(edges[i].x); } queue<int> q;memset(ans,-1,sizeof ans); forn(i,0,N - 1) if(!deg[i]) q.push(i),ans[i] = 0; while(!q.empty()) { int u = q.front();q.pop(); for(auto& v : E[u]) { if(ans[v] != -1) continue; --deg[v]; if(ans[u] == 0) ans[v] = 1,q.push(v); else if(!deg[v]) ans[v] = 0,q.push(v); } } forn(i,1,n) { if(ans[edges[i].y] == -1) puts("Draw"); else if(ans[edges[i].y] == 0) puts("Takahashi"); else puts("Aoki"); } return 0; }
F - Deforestation
這個題如果只是要求最小值,是一個出爛的玩意,但是要求方案數,手玩一下發現原來求最小值的貪心對於求方案數沒有任何貢獻.
考慮從題目本身做:對於每個\(H_i\)對於答案的貢獻是多少?當刪去\(H_i\)的時候一定會產生\(H_i\)的貢獻,當刪去\(H_{i+1}\)的時候,\(H_i\)會產生貢獻當且僅當\(H_{i+1}\)是先於\(H_i\)被砍的.同理當刪去\(H_{i-1}\)的時候,\(H_i\)會產生貢獻當且僅當\(H_{i-1}\)先於\(H_i\)被刪掉.
從前往後考慮每個元素\(i \in [1,n-1]\):
- 若\(H_i < H_{i+1}\),那麼\(H_i\)的刪去時間應該晚於\(H_{i+1}\),否則會讓\(H_{i+1}\)往答案貢獻兩次而不是\(H_i\)貢獻兩次(顯然後者更優).
- 若\(H_i > H_{i+1}\),那麼\(H_i\)的刪去時間應該早於\(H_{i+1}\),原因同上.
- 若兩者相同,顯然先後不會再對貢獻有影響.
如此可以考慮一個dp
:
- 狀態:\(f[i][j]\)表示分配前\(i\)個元素,末尾元素被安排在\(j\)位置上的方案數.
- 入口:\(f[1][1] = 1\)其他為\(0\).
- 轉移:對於\(i \in[2,n]\),若\(H_i == H_{i-1}\)則\(f[i][j] = \sum\limits_{k = 1}^i f[i - 1][k]\),若\(H_i < H_{i-1}\),則\(f[i][j] = \sum\limits_{k = j}^i f[i - 1][k]\),若\(H_i > H_{i-1}\),則\(f[i][j] = \sum\limits_{k = 1}^{j - 1} f[i - 1][k]\).當\(j == k\)時,認為是\(H_i\)插入到\(H_{i-1}\)的前面.
- 出口:\(ans = \sum\limits_{k = 1}^n f[n][i]\).
轉移部分,在每次處理完\(i\)維的轉移後,記錄本維狀態的字首和,即可將轉移複雜度降到可以承受的\(O(n^2)\).
注意給\(sum[1] = 1\)初值.
($j == k的情況如何處理有些怪,以後補)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 4005,MOD = 1e9+7;
int H[N],f[N][N],sum[N];
int main()
{
int n;scanf("%d",&n);
forn(i,1,n) scanf("%d",&H[i]);
f[1][1] = sum[1] = 1;
forn(i,2,n)
{
forn(j,1,i)
{
if(H[i] == H[i - 1]) f[i][j] = sum[i - 1];
else if(H[i] < H[i - 1]) f[i][j] = ((sum[i - 1] - sum[j - 1]) % MOD + MOD) % MOD;
else f[i][j] = sum[j - 1];
}
forn(j,1,n) sum[j] = (f[i][j] + sum[j - 1]) % MOD;
}
int res = 0;
forn(i,1,n) res = (res + f[n][i]) % MOD;
printf("%d\n",res);
return 0;
}