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;
	
}
posted @ 2022-05-25 13:33  EnderDeer  阅读(30)  评论(0编辑  收藏  举报
Live2D