Educational Codeforces Round 168 (Rated for Div. 2)

A. Strong Password

解析:因为要让最终生成的密码最大,所以我们尝试是否在字符串中能找到两个相邻相同的字符,用一个不同的字符给它们隔开,如果没有找到,那就可以随便插入,但是要保证不能插入与前后相同的字符,所以我们可以在末尾插入一个与末尾字符不同的字符。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+10;
const int mod=998244353;
void solved() {
	string s;
	cin>>s;
	string s1;
	int flag=0;
	for (int i=0; i<s.size()-1; i++) {
		s1+=s[i];
		if (s[i]==s[i+1]&&!flag) {
			if ('a'==s[i]) {
				s1+='b';
			} else {
				s1+='a';
			}
			flag=1;
		}
	}
	s1+=s[s.size()-1];
	if (flag) {
		cout<<s1<<"\n";
	} else {
		if ('a'==s1[s1.size()-1]) {
			s1+='b';
		} else {
			s1+='a';
		}
		cout<<s1<<"\n";
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int t=1;
	cin>>t;
	while (t--) {
		solved();
	}
}

B. Make Three Regions

解析:赛前没找到草稿本,正常比赛都是自己空想,这道题没画图当时直接写成史了。因为题目说给出的网格中最多只有一个相连区域,那么我们需要做到阻塞一个格子多出两个区域。我们画图可以发现只有像下面这种图才能做到
在这里插入图片描述
把黄色的区域堵住后,这时就会形成三个区域。
在这里插入图片描述
两排都可能出现这种情况,但是这时候要判断被点黄区域对面得是’x.x’这种,并且黄点区域左右不能出现‘x’,因为如果对面符合’x.x’,黄色区域左右如果有’x’,那就直接封闭了,不满足要求,还有就是第一个和最后一个不能点。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+10;
const int mod=998244353;
string s[3];
void solved() {
	int n;
	cin>>n;
	for (int i=1; i<=2; i++) {
		cin>>s[i];
		s[i]='&'+s[i]+'&';
	}
	int ans=0;
	for (int i=2; i<=n-1; i++) {
		if (s[1][i]=='.'&&s[1][i-1]!='x'&&s[1][i+1]!='x'&&s[2][i]=='.') {
			if (s[2][i-1]=='x'&&s[2][i+1]=='x') {
				ans++;
			}
		}
		if (s[2][i]=='.'&&s[2][i-1]!='x'&&s[2][i+1]!='x'&&s[1][i]=='.') {
			if (s[1][i-1]=='x'&&s[1][i+1]=='x') {
				ans++;
			}
		}
	}
	cout<<ans<<"\n";
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int t=1;
	cin>>t;
	while (t--) {
		solved();
	}
}

C. Even Positions

解析:要让成本尽可能小,那么让)尽早出现。赛时我还没看见奇数位置都是’_‘,偶数位置都是’(‘或’)',当时看样例硬写代码过的,我还在想我为什么没有被hack呢。有了这个条件就好做了,我们建一个栈将(放进去,遇到__如果栈是空的,则__为左扩号,遇到)直接从栈里取一个出来就行。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+10;
const int mod=998244353;
void solved() {
	int n;
	cin>>n;
	string s;
	cin>>s;
	stack<int>stk;
	int ans=0;
	for (int i=0; i<s.size(); i++) {
		if (s[i]=='(') {
			stk.push(i);
		} else if (s[i]=='_') {
			if (!stk.empty()) {
				int pos=stk.top();
				stk.pop();
				ans+=(i-pos);
			} else {
				stk.push(i);
			}
		} else {
			int pos=stk.top();
			stk.pop();
			ans+=(i-pos);
		}
	}
	cout<<ans<<"\n";
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int t=1;
	cin>>t;
	while (t--) {
		solved();
	}
}

D. Maximize the Root

解析:这道题有两种方法,一个是二分答案,二分根结点1的值,这里把题中的操作称为借,从上往下传需要借的点数,如果将需要借的传到最后,还不满足那么这个答案就不符合(注意二分的时候向下传小心溢出,因为每次是*2增长)。
另一个是树形DP,由于有一层假如需要借,那么它的子树都需要减一,所以我们需要从下往上平均一下,但是这个平均只能对于这个结点的子结点大,本身小,因为可以向下借,将本身变大。如果本身大于平均就不需要平均了,还得更新可以借的,因为要保证父结点要的时候大家都能接出去。
在这里插入图片描述
二分:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+10;
const int mod=998244353;
int a[N];
vector<int>e[N];
int n;
int flag;
int  dfs(int x,int fa,int mid) {
	int ans=0;
	if (mid>1e9) {//不加会溢出
		flag=0;
		return 0;
	}
	if (x==1) {
		ans=mid;
	} else {
		if (a[x]-mid<0) {
			ans=(mid-a[x])+mid;
		} else {
			ans=mid;
		}
	}
	int xx=1;
	for (auto v:e[x]) {
		if (v==fa) {
			continue;
		}
		xx=0;
		dfs(v,x,ans);
	}
	if (xx==1) {
		if (a[x]-mid<0) {
			flag=0;
			return 0;
		}
	}
	return 0;
}

int check(int x) {
	flag=1;
	dfs(1,-1,x-a[1]);
	if (flag!=0) {
		return 1;
	}
	return 0;
}
void solved() {
	cin>>n;
	for (int i=1; i<=n; i++) {
		e[i].clear();
		cin>>a[i];
	}
	for (int i=2; i<=n; i++) {
		int x;
		cin>>x;
		e[x].push_back(i);
	}
	int l=a[1]-1,r=3e9;
	while (l+1<r) {
		int mid=(l+r)/2;
		if (check(mid)) {
			l=mid;
		} else {
			r=mid;
		}
	}
	cout<<l<<"\n";
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int t=1;
	cin>>t;
	while (t--) {
		solved();
	}
}

树形DP

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+10;
const int mod=998244353;
int a[N];
vector<int>e[N];
int n;
int flag;
int dp[N];
int dfs(int x,int fa) {
	int t=1e18;
	for (auto v:e[x]) {
		if (v==fa) {
			continue;
		}
		t=min(t,dfs(v,x));

	}
	if (x==1){
		return t;
	}
	if (t>=1e18){
		return dp[x];
	}
	if (t>dp[x]) {
		dp[x]=(t+dp[x])/2;
	}
	t=min(dp[x],t);
	return t;
}
void solved() {
	cin>>n;
	for (int i=1; i<=n; i++) {
		e[i].clear();
		cin>>dp[i];
	}
	for (int i=2; i<=n; i++) {
		int x;
		cin>>x;
		e[x].push_back(i);
	}
	int ans=dp[1]+dfs(1,-1);
	cout<<ans<<"\n";
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int t=1;
	cin>>t;
	while (t--) {
		solved();
	}
}

E. Level Up

解析:
我们可以发现对于每个怪物有一个不能挑战k的阈值,当大于这个阈值都会与怪物战斗。
于是我们就可以二分找到每一个怪物的阈值,check里面去找能战斗的数量。
但正常从左到右去找的话会超时。
所以我们需要想一个方法去优化,相当于很快速的找到小于等于k的个数。
于是我们可以用到权值线段树,因为权值线段树的权值是按顺序的,我们正好用来存每一个k的数量。
查找的时候只需要加上当前每个k对应的下标数。
最后将二分的这个答案存到b[i]中,后面查询判断与b[i]的关系即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
const int mod=998244353;
#define lson now<<1
#define rson now<<1 | 1
int a[N];
int tree[4*N];
int b[N];
int ans=0;
void insert(int now,int l,int r,int x,int op){
	if (l==r){
		tree[now]+=op;
		return;
	}
	int mid=(l+r)/2;
	if (x<=mid){
		insert(lson,l,mid,x,op);
	}else{
		insert(rson,mid+1,r,x,op);
	}
	tree[now]=tree[lson]+tree[rson];
}
void querry(int now,int l,int r,int x){
	if (l==r){
		ans+=tree[now];
		return;
	}
	int mid=(l+r)/2;
	if (x<=mid){
		querry(lson,l,mid,x);
	}else{
		ans+=tree[lson];
		querry(rson,mid+1,r,x);
	}
}
void solved() {
	int n,q;
	cin>>n>>q;
	for (int i=1;i<=n;i++){
		cin>>a[i];
	}
	//build(1,1,200000);
	for (int i=1;i<=n;i++){
		int l=0,r=200001;
		while (l+1<r){
			int mid=(l+r)/2;//二分最小能与第i个怪物战斗的k
			ans=0;
			querry(1,1,200000,mid);
			if (ans/mid+1<=a[i]){
				r=mid;
			}else{
				l=mid;
			}	
		}
		b[i]=r;
		insert(1,1,200000,r,1);
	}
	while (q--){
		int i,x;
		cin>>i>>x;
		if (b[i]<=x){
			cout<<"YES\n";
		}else{
			cout<<"NO\n";
		}
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int t=1;
	//cin>>t;
	while (t--) {
		solved();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值