1. 程式人生 > 其它 >AtCoder Beginner Contest 252

AtCoder Beginner Contest 252

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;
	
}