CF Educational67

ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
题目链接:https://codeforces.com/contest/1187

A

题意: 有n个蛋,蛋里放着物品A,或者物品B,或者物品A和B,A有s个,B有t个,问至少拿多少个蛋才能拿到A和B。

解法:模拟~画一条线,长度为n,A从左边开始,B从右边开始,中间是重叠部分,左右两边是不重叠部分,取大的一边,再加一就是答案了…

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int T;
int main(){
	cin>>T;
	while(T--){
		ll n,s,t;
		cin>>n>>s>>t;
		ll ans = max(n-s,n-t)+1;
		cout<<ans<<endl; 
	} 
	return 0;
}

B

题意:有一个字符串s,有N个字符串t,问要包含字符串t的所有字母,至少去字符串s的前缀多长。

解法:统计字符串s的信息,cnt[‘a’][x]=num:表示a出现x次在位置num.如cnt[‘a’][1000]=1005表示a的第1000次 出现在位置1005
对于每个t,统计它的字母出现的次数。然后根据这些次数在cnt里面查找答案。

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N=2e5+5;
int n,m,cnt[33][N],cnt1[33],cnt2[33];
char s[N],t[N];
void init(){
	scanf("%d",&n);
	scanf("%s",s+1);
	for(int i=1; i<=n; i++){
		cnt[s[i]-'a'][++cnt1[s[i]-'a']]=i; // s[i]出现cnt1次在位置i 
	}
}
int main(){
	init();
	scanf("%d",&m);
	while(m--){
		scanf("%s",t+1);
		int len = strlen(t+1);
		int ans = 0;
		memset(cnt2,0,sizeof(cnt2));
		for(int i=1; i<=len; ++i){
			cnt2[t[i]-'a']++;
		}
		for(int i=0; i<26; i++){
			ans=max(ans, cnt[i][cnt2[i]]);
		}
		cout<<ans<<endl;
	}
	return 0;
}

C

题目:有一个长度为n的未知数列A,这里有m条信息,包含三个信息(t,l,r)
当t=1,表示A[l,r]是上升数列
当t=0,表示A[l,r]是非上升数列,也就是这是有一对相邻的对前者大于后者
如果数列存在,输出数列,否则输出NO

解法:n比较小,只有1000。
对于t=1来说,这是必须上升的,所以我们可以先把t=1的情况处理了,把上升的位置全都标记出来s[i]=1表示A[i]<A[i-1],数据比较小,直接暴力标记了,也可以用差分,最后再一次性标记

对于t=0来说,我们只有能找到一个s[i]!=1的就行 l < = i < r l<=i<r l<=i<r,否则就无法构造数列

然后构造数列

非暴力法,就是差分之后确定了上升序列的位置后,然后构造数列
接着使用t=0的来进行验证

code略微暴力

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N=1e3+5;
int n,m,s[N],ans[N];
struct node{
	int t,l,r;
	bool operator < (const node &p)const{
		return t>p.t;
	} 
}a[N];
int main(){
	cin>>n>>m;
	fo(i,1,m){
		int t,l,r;
		cin>>a[i].t>>a[i].l>>a[i].r;
		
	}
	sort(a+1,a+m+1);
	fo(i,1,m){
		if(a[i].t==1){
			for(int j=a[i].l; j<a[i].r; j++){
				s[j]=1; // 上升 
			}
		}else{
			bool flag = 0;
			for(int j=a[i].l; j<a[i].r; j++){
				if(s[j]!=1){ // 只要有非上升就标记为下降 
					s[j]=2;
					flag = 1;
					break;
				}
			}
			if(!flag){
				cout<<"NO"<<endl;
				return 0;
			}
		} 
	}
	ans[1]=1005;
	for(int i=2; i<=n; i++){
		if(s[i-1]==2){
			ans[i]=ans[i-1]-1; 
		} else{
			ans[i]=ans[i-1]+1;
		}
	}
	cout<<"YES"<<endl;
	fo(i,1,n){
		cout<<ans[i]<<" "; 
	}
	return 0;
}

code差分优化

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N=1e3+5;
int n,m,s[N],ans[N],d[N];
struct node{
	int t,l,r;
	bool operator < (const node &p)const{
		return t>p.t;
	} 
}a[N];
int main(){
	cin>>n>>m;
	fo(i,1,m){
		int t,l,r;
		cin>>a[i].t>>a[i].l>>a[i].r;
		
	}
	sort(a+1,a+m+1);
	fo(i,1,m){
		if(a[i].t==1){
			d[a[i].l] += 1;
			d[a[i].r] -= 1; 
		}
	}
	
	fo(i,1,n){
		d[i] += d[i-1];
	}
	// 构造答案 
	ans[1]=n;
	for(int i=2; i<=n; i++){
		if(d[i-1]>0){
			ans[i]=ans[i-1]+1;
		}else{
			ans[i]=ans[i-1]-1;
		}
	}
	
	// 检查答案
	fo(i,1,m){
		if(a[i].t==0){
			if(ans[a[i].r]-ans[a[i].l] == a[i].r-a[i].l){
				// 完全上升 
				cout<<"NO"<<endl; 
				return 0;
			}
		}
	} 
	cout<<"YES"<<endl;
	fo(i,1,n){
		cout<<ans[i]<<" "; 
	}
	return 0;
}

E

题意:有一个无向图(树),第一次选择一个点作为根,
(之后选择的点是已经选过的点的子节点)每选择一个点,对答案的贡献为树的大小
问:怎么选使得答案最大

解法:
暴力:n遍dfs统计出来(T)
优化:二次扫描和换根法

二次换根法简单介绍:先一次统计出确定了一个根的答案(由所有子节点的答案加自己的和),换根:把新根的答案(在旧根里是子儿子,在新根里它已经成为一棵树的根了)去掉,然后加上旧根的答案(旧根原来是一刻树的根,现在变成子节点了)
code

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N=2e5+5,M=N*2;
int n;
int head[N],Next[M],ver[M],tot;
void add(int x, int y){
	ver[++tot]=y;
	Next[tot]=head[x], head[x]=tot;
}
int son[N];
ll ans;
void dfs(int x, int fa){
	son[x]=1;
	for(int i=head[x]; i; i=Next[i]){
		int y=ver[i];
		if(y==fa)continue;
		dfs(y,x);
		son[x]+=son[y];
	}
	ans+=son[x]; // 每个选择的点对答案的贡献 
}
void DFS(int x, int fa, ll now){
	ans = max(ans, now);
	for(int i=head[x]; i; i=Next[i]){
		int y = ver[i];
		if(y==fa)continue;
		DFS(y, x, now-son[y]+(n-son[y]));
	}
} 
int main(){
	cin>>n;
	int x,y;
	fo(i,2,n){
		cin>>x>>y;
		add(x,y);
		add(y,x);
	} 
	dfs(1,-1); 
	DFS(1,-1,ans);
	cout<<ans<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值