AtCoder Beginner Contest 252
阿新 • • 發佈:2022-05-25
Problem A,B
一分鐘通過,此處不贅述。
Problem C
由於只有 9 個字元,所以分開處理。
對於一個字元,最好的方案是到一個時間點,有任何一個字串這個位置有這個字元,就直接選。
但是實際上會出現在多個字串,有一個位置的字元在多個字串出現,意思是 \(s[1][i] == s[k][i]\) 的情況。
這很好處理。每一次只能選擇一個,那麼相同的位置只能在 10 秒後再選擇。
所以給每個字元開一個vector,把位置推進去,相同的位置就 +10 ,然後排序,選前 n 個最優。
#include<bits/stdc++.h> #define int long long #define mem(x,y) memset(x,y,sizeof(x)) #define frein freopen("in.in","r",stdin) #define freout freopen("out.out","w",stdout) using namespace std; int read(){ int s = 0,w = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-')w = -1;ch = getchar();} while(ch >= '0' && ch <= '9')s = s * 10 + ch - '0',ch = getchar(); return s * w; } int n; string s; vector <int> q[11]; int ans = 1e18; int bk[110][110]; signed main(){ cin>>n; for(int i = 1;i <= n;i ++){ cin>>s; s = '#' + s; for(int j = 1;j <= 10;j ++){ if(!bk[s[j] - '0'][j - 1])q[s[j] - '0'].push_back(j - 1); else q[s[j] - '0'].push_back(j - 1 + bk[s[j] - '0'][j - 1]); bk[s[j] - '0'][j - 1] += 10; } } for(int j = 0;j <= 9;j ++)sort(q[j].begin(),q[j].end()); for(int j = 0;j <= 9;j ++){ int s = 2147483647; if(q[j].size() < n)continue; int cnt = 0; for(int k = 0;k < q[j].size();k ++){ if(k == 0 || q[j][k] != q[j][k - 1]){ cnt ++; s = q[j][k]; if(cnt == n)break; } } if(cnt == n)ans = min(ans,s); } cout<<ans; }
Problem D
容斥。
假設三個數為 \(a,b,c\) ,列舉 \(b\) ,然後做一個字首字尾值域陣列。
答案就是 \(a \neq b\) 且 \(c \neq b\) 的個數,減去滿足以上一條情況下 \(a = c\) 的個數,再加上滿足以上兩條情況下 \(a = b\) 且 \(b = c\) 的情況。
#include<bits/stdc++.h> #define int long long #define mem(x,y) memset(x,y,sizeof(x)) #define frein freopen("in.in","r",stdin) #define freout freopen("out.out","w",stdout) using namespace std; int read(){ int s = 0,w = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-')w = -1;ch = getchar();} while(ch >= '0' && ch <= '9')s = s * 10 + ch - '0',ch = getchar(); return s * w; } int n; int pre[1000010]; int nxt[1000010]; int a[1000010]; int ans; int now; signed main(){ cin>>n; for(int i = 1;i <= n;i ++)a[i] = read(); for(int i = 2;i <= n;i ++)nxt[a[i]] ++; for(int i = 2;i <= n - 1;i ++){ nxt[a[i]] --; now -= pre[a[i]]; now += nxt[a[i - 1]]; pre[a[i - 1]] ++; ans += (((i - 1 - pre[a[i]]) * (n - i - nxt[a[i]]))); ans -= now; ans += pre[a[i]] * nxt[a[i]]; } cout<<ans; }
Problem E
最短路徑樹模板。
dijkstra 會用 \(n - 1\) 條邊更新。
這些邊都是在某條最短路徑上的。
所以這樣必然優秀。
跑一遍 dijkstra ,把用作更新的 \(n - 1\) 條邊記錄下來,即為答案。
/* 深山踏紅葉,耳畔聞鹿鳴 飄搖風雨中,睹物思故鄉 可嘆,落葉飄零 */ #include<bits/stdc++.h> #define int long long #define mem(x,y) memset(x,y,sizeof(x)) #define frein freopen("in.in","r",stdin) #define freout freopen("out.out","w",stdout) #define debug(x) cout << (#x) << " = " << x << endl; using namespace std; int read(){ int s = 0,w = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-')w = -1;ch = getchar();} while(ch >= '0' && ch <= '9')s = s * 10 + ch - '0',ch = getchar(); return s * w; } struct node{ int to; int nxt; int w; int id; }e[1000010]; int cnt,head[1000010]; int n,m; int dis[1000010]; bool bk[1000010]; int lst[1000010]; vector <int> ans; void add(int x,int y,int w,int id){ cnt ++; e[cnt].to = y; e[cnt].nxt = head[x]; e[cnt].w = w; e[cnt].id = id; head[x] = cnt; } void dijkstra(){ priority_queue <pair <int,int> > q; for(int i = 1;i <= n;i ++)dis[i] = 1e18; dis[1] = 0; q.push({0,1}); while(!q.empty()){ pair <int,int> now = q.top(); q.pop(); int x = now.second; if(bk[x])continue; bk[x] = true; for(int i = head[x];i;i = e[i].nxt){ int v = e[i].to; if(dis[v] > dis[x] + e[i].w){ dis[v] = dis[x] + e[i].w; if(!bk[v])q.push({-dis[v],v}); lst[v] = e[i].id; } } } } signed main(){ cin>>n>>m; for(int i = 1;i <= m;i ++){ int x,y,w; x = read(),y = read(),w = read(); add(x,y,w,i); add(y,x,w,i); } dijkstra(); for(int i = 2;i <= n;i ++)printf("%lld ",lst[i]); }
Problem F
合併果子的逆過程,只不過多了個裁剪多餘的 kunbar 。
把這個大 kunbar 也推進佇列就行了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
priority_queue <int,vector<int>,greater<int> > q;
int n;
int ans;
signed main(){
int m;
cin>>n>>m;
for(int i = 1;i <= n;i ++){
int x;
cin>>x;
q.push(x);
m -= x;
}
if(m){
q.push(m);
}
while(q.size() != 1){
int a = q.top();
q.pop();
int b = q.top();
q.pop();
q.push(a + b);
ans += a + b;
}
cout<<ans;
return 0;
}
Problem G
區間dp。
設定 \(dp_{ij}\) 為 \([i,j]\) 這個區間組成的 dfs 序樹個數。
分類討論,如果要合併區間的話,有兩種情況。
第一種是把整個樹接上去。
第二種是把樹接在大區間根的同層。
第二種就是 \([i + 1,j]\) 這個區間的個數了。
第一種的話,找個點作為連線口,然後把大區間拆開,乘法原理就行了。
要判斷是否可以接上去同層,也就是判斷大小,因為題目先遍歷編號小的點。
#include<bits/stdc++.h>
#define int long long
#define mem(x,y) memset(x,y,sizeof(x))
#define frein freopen("in.in","r",stdin)
#define freout freopen("out.out","w",stdout)
#define debug(x) cout << (#x) << " = " << x << endl;
using namespace std;
int read(){
int s = 0,w = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')w = -1;ch = getchar();}
while(ch >= '0' && ch <= '9')s = s * 10 + ch - '0',ch = getchar();
return s * w;
}
int n;
int a[1000010];
int dp[510][510];
const int mod = 998244353;
signed main(){
cin>>n;
for(int i = 1;i <= n;i ++)a[i] = read();
for(int i = 1;i <= n;i ++)dp[i][i] = 1;
for(int len = 2;len <= n;len ++)
for(int i = 1;i <= n;i ++){
int j = i + len - 1;
if(j > n)break;
dp[i][j] = (dp[i][j] + dp[i + 1][j]) % mod;
for(int k = i + 1;k <= j - 1;k ++){
if(a[i + 1] < a[k + 1])
dp[i][j] = (dp[i][j] + dp[i + 1][k] * dp[k][j] % mod) % mod;
}
}
cout<<(dp[1][n] % mod + mod) % mod<<endl;
}