The 2022 ICPC Asia Regionals Online Contest (I)

C Delete the Tree(结论) 

给出一棵树,有两种操作:1、一种是删除一个节点及所有连向它的边;2、对于度严格为2的点,可以将它去掉,在它的子节点和父节点之间连边。目的是删除整棵树,尽量少用1操作,问最少需要几次1操作。

思路:大胆猜想!队友手摸几组发现是度为1的点,即所有的叶节点。也是可以证明的,我们缩点只能缩其中的节点,叶子结点是没有办法缩的。

AC Code:

#include <bits/stdc++.h>

typedef long long ll;
const int N=1e6+5;
int t,n;
int cnt[N];

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin>>t;
	while(t--){
		std::cin>>n;
		for(int i=1;i<=n;i++) cnt[i]=0;
		for(int i=1;i<n;i++){
			int u,v;
			std::cin>>u>>v;
			cnt[u]++,cnt[v]++;
		}
		if(n==1){
			std::cout<<1<<'\n';
			continue;
		}
		int ans=0;
		for(int i=1;i<=n;i++){
			if(cnt[i]==1) ans++;
		}
		std::cout<<ans<<'\n';
	}
	return 0;
}

os:辣鸡PTA!它卡memset!脑血栓几年才能想出这种怪东西啊!!!

H Step Debugging(模拟,栈)

给出了一种语言,求其中library计算了多少次。

思路:模拟。。。

AC Code:

#include <bits/stdc++.h>

typedef long long ll;
const int N=1e6+5;
const int mod=20220911;
std::string s;

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::stack<ll>sta;
	ll ans=0;
	while(std::cin>>s){
		if(s=="fin") break;
		if(s=="library"){
			if(sta.empty()) ans++;
			else sta.top()++;
		}
		else if(s=="repeat") sta.push(0);
		else if(s[0]>='0'&&s[0]<='9'){
			ll num=stoll(s);
			ll dd=sta.top();
			sta.pop();
			dd=(dd*num)%mod;
			if(sta.empty()) ans=(ans+dd)%mod;
			else sta.top()=(sta.top()+dd)%mod;
		}
	}
	while(!sta.empty()){
		ans=(ans+sta.top())%mod;
		sta.pop();
	}
	std::cout<<ans<<'\n';
	return 0;
}

 os:真的,这个题真的气死我了,我们D都搞出来了这个题写不出来,写了将近四个半小时。。

D Find the Number(打表+二分;贪心)

给出一个范围,找到这个范围内任意一个,满足条件的数字:二进制中后缀0和全部1的个数相等的数字,若没有则输出-1。

思路:(1)因为数据范围是1e9,也就32位,可以直接在范围内打表,然后二分答案。赛后才想到这种“暴力”做法,感觉很妙;

(2)贪心做法。从下限开始找,如果下限数字满足要求,直接输出即可;若不满足,判断是0多还是1多的情况,若是0多则一定存在加一个1使得数字满足情况的条件(前提是这个数不超过上限);若是1多,则在最低为1的位+1,使1减少,再判断0多还是1多的情况,继续按照之前的分类操作。

AC Code:

(1)康康严格鸽的题解叭,ICPC 网络赛 D 的几种枚举方式 - 知乎 (zhihu.com)

(2)

#include <bits/stdc++.h>

typedef long long ll;
#define int long long
ll t,l,r;

int get0(ll x){
	int cnt=0;
	while(x%2==0) x>>=1,cnt++;
	return cnt;
}

int get1(ll x){
	int cnt=0;
	while(x){
		if(x&1) cnt++;
		x>>=1;
	}
	return cnt;
}

int lowbit(ll x){
	return x&(-x);
}

ll cal0(ll x);
ll cal1(ll x);

signed main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin>>t;
	while(t--){
		std::cin>>l>>r;
		int a=get0(l);
		int b=get1(l);
		if(a==b){
			std::cout<<l<<'\n';
			continue;
		}
		if(a>b) std::cout<<cal0(l)<<'\n';
		else std::cout<<cal1(l)<<'\n';
	}
	return 0;
}

ll cal0(ll x){
	int cnt0=get0(x);
	int cnt1=get1(x);
	x+=(1ll<<(cnt1+1));
	if(x>r) return -1;
	cnt0=get0(x);
	cnt1=get1(x);
	if(cnt0==cnt1) return x;
	if(cnt0>cnt1) return cal0(x);
	else return cal1(x);
}

ll cal1(ll x){
	int xx=lowbit(x);
	x+=xx;
	if(x>r) return -1;
	int cnt0=get0(x);
	int cnt1=get1(x);
	if(cnt0==cnt1) return x;
	if(cnt0>cnt1) return cal0(x);
	else return cal1(x);
}

os:这个题写的非常的顺。。。很可惜啊,可惜我们签到题没出。。。

A 01 Sequence

给出一个01序列,每次次操作可以删去一个1和它两侧的0,或者将一个位置的数字倒置,q次询问,对于给出的区间l,r,问经过最少几次倒置操作使得可以将序列删为空。

思路:有个结论,连续的一段1所需要的操作次数是1的个数/2,对于一个区间,如果t是这个区间的操作次数,那答案是max(0,len/3-t),len是区间长度。对于求贡献,即区间需要的操作数,可以用前缀和处理,每次对于一个区间找在其中最靠近两侧0的位置,这样其中的区间的贡献可以直接相减得到,其他的位置的1都是相连的,可以直接计算。

AC Code:

#include <bits/stdc++.h>

typedef long long ll;
const int N=1e6+5;
int n,q;
std::string s;
int pre[N],suf[N],dif[N];

int cal(int l,int r){
	int ll=l+pre[l],rr=r-suf[r];
	if(ll>=rr) return (r-l+1)>>1;
	return dif[rr]-dif[ll-1]+(pre[l]+suf[r]+1)/2;
}

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin>>n>>q;
	std::cin>>s;
	s=' '+s;
	int last=-1;
	for(int i=1;i<=n;i++){
		if(s[i]=='1'){
			if(last!=i-1) dif[i]=1,last=i;
			else last=-1;
		}
		dif[i]+=dif[i-1];
	}
	for(int i=1;i<=n;i++){
		if(s[i]=='1') suf[i]=suf[i-1]+1;
		else suf[i]=0;
	}
	for(int i=n;i>=1;i--){
		if(s[i]=='1') pre[i]=pre[i+1]+1;
		else pre[i]=0;
	}
	while(q--){
		int l,r;
		std::cin>>l>>r;
		int t=cal(l,r);
		int len=(r-l+1)/3;
		std::cout<<std::max(0,len-t)<<'\n';
	}
	return 0;
}

补不动啦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值