Codeforces Global Round 13(A~E ) 补题

链接

https://codeforces.com/contest/1491

A. K-th Largest Value

(rating:800)
题意:
给一个仅含0,1的序列 a, 进行以下两个操作:

  1. 把ai 如果是0换成1,如果是1换成0
  2. 输出第k大元素

思路:
记录下当前有几个0几个1即可。

B. Minimal Cost

(rating:1200)
在这里插入图片描述

题意:
初始位置在(1,0) ,要到(n,106+1 ).
每行有且仅有一个障碍物 。 移动障碍物时,横着移动一格需要耗费体力v, 竖着移动一个需要体力u 。 问能到达终点 最少体力花费。(障碍物只能在第1列至第106 列之间)
思路:
相邻两行的障碍物,如果列数相差2以上,无需移动,直接穿过去即可,耗费体力 = 0。
相邻两行的障碍物,如果列数相差1,耗费体力 = min(u,v)。
相邻两行的障碍物如果列数相同 , 耗费体力 = min(u+v,v+v)。
对所有相邻行判断取最小值。

C. Pekora and Trampoline(贪心)

(rating:1700)
题意:
有从左到右n个蹦床 , 每个蹦床的弹性是Si 。 当落在第i个蹦床上时 , 会被弹到第i+Si个蹦床上 , 直到弹出这n个蹦床 。
每从一个蹦床上弹起后,该蹦床的弹性会衰减 1 , 对于Si = 1,则不再衰减 。
问最少弹多少个回合能使所有蹦床弹性都为1 .
思路:
不难发现 , 越靠左的非1蹦床早晚都得跳掉 , 而且弹起高度只取决于当前蹦床 。 那么贪心即可 。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn = 5050;
ll a[maxn];
ll n;
ll b[maxn];
int main(){
	int t;
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
		ll ans = 0;
		for(int i=1;i<=n;i++){ 
			if(a[i]!=1){
				ans += max((ll)0,(a[i]-b[i]-1));
			}
			for(int j=n;j>i+1;j--){
				if(j-i<=a[i])
				b[j]++;
			}
			ll z = b[i] - a[i] + 1;
			b[i+1] += max(z,(ll)0);
		}
		cout<<ans<<endl;
		for(int i=1;i<=n+5;i++)
		b[i] = 0;
	}
}

D. Zookeeper and The Infinite Zoo(数学)

(rating:1800)
在这里插入图片描述

题意:
若u&v = v , 就在u —> u+v 上增加一条边 。
给两个数a,b . 问a能否到b 。
思路:
u&v==v
那么u是 0 的地方v不能是1 。
那么u+v 中1的位置必然不小于 u中1的位置 。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5+5;
    bool check(int u,int v){
    	int a=0,b=0;
    	for(int i=0;i<=32;i++){
    		if(u>>i&1) a++;
    		if(v>>i&1) b++;
    		if(b>a)
    		return 0;
    	}
    	return 1;
    } 
    int main(){
    	int t;
    	cin>>t;
    	while(t--){
    		int u,v;
    		scanf("%d %d",&u,&v);
    		if(u>v)
    		printf("NO\n");
    		else if(u==v)
    		printf("YES\n");
    		else{
    			if(check(u,v)==1)
    			printf("YES\n");
    			else
    			printf("NO\n");
    		}
    	}
    	return 0;
    } 

E. Fib-tree(树论)

(rating:2400)
题意:
判断一棵树是否是fib(斐波那契)树。
fib树定义如下:
1.fib树的节点个数是斐波那契数 。
2.节点数大于1的fib树可以分成两棵fib树 。 (递归定义)
思路:
挺难但很好的一道题 , 从这道题学会了很多。。
主要就是两个问题,
一个是链式前向星的删边 cut[i] = cut[i^1] = 1,
一个是子树节点的存储 。
方法就是每次找到满足分割条件的节点u时,把节点u与节点fa[u]看成两棵新树的根 , 继续找size满足Fibonacci的节点 。 由于每次dfs都是新的树根 , 因此每次dfs都必须对size做重新处理。
代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const ll maxn = 5e5+20;
    struct E{
    	ll to;
    	ll nxt;
    }e[maxn<<1];
    ll head[maxn],tot=-1;
    ll cut[maxn];
    void add_edge(ll u,ll v){
    	e[++tot].to = v;
    	e[tot].nxt = head[u];
    	head[u] = tot;
    }
    ll sz[maxn];
    ll fib[maxn<<2],fac[maxn];
    void pre(){
    	fib[1] = 1,fib[2] = 1,fac[1] = 1;
    	for(ll i=3;i<=40;i++){
    		fib[i] = fib[i-1]+fib[i-2];
    		if(fib[i]>=200005)
    		break;
    		fac[fib[i]] = 1;
    		//cout<<fib[i]<<endl;
    	}
    }
    ll now = 0;
    ll loc = 0;ll fa[maxn];
    void dfs(ll u,ll f,ll siz,ll num){
    	//cout<<u<<endl;
    	fa[u] = f;
    	sz[u] = 1;
    	for(ll i=head[u];i>=0;i=e[i].nxt){
    		ll v = e[i].to;
    		if(v==f) continue;
    		if(cut[i]==1) continue;
    		//cout<<v<<endl;
    		dfs(v,u,siz,i);
    		sz[u] += sz[v];
    	}
    	if(fac[sz[u]]==1&&fac[siz-sz[u]]==1){
    		//cout<<"ok"<<endl;
    			now = u;
    			//cout<<size[u]<<endl;
    			loc = num;
    		}
    }
    void solve(ll root,ll siz){
    	//cout<<now<<endl;
    	if(siz==1)
    	return;
    	now = 0;
    	dfs(root,0,siz,-1);
    	if(now==0){
    		cout<<"NO"<<endl;
    		exit(0);
    	}
    	cut[loc] = cut[loc^1] = 1;
    	ll fa_now = fa[now] , fa_size = siz - sz[now];
    	solve(now,sz[now]);
    	solve(fa_now,fa_size);
    }
    int main(){
    	memset(head,-1,sizeof(head));
    	pre();
    	ll n;
    	cin>>n;
    	for(int i=1;i<n;i++){
    		ll x,y;
    		scanf("%lld %lld",&x,&y);
    		add_edge(x,y);
    		add_edge(y,x);
    	}
    	if(fac[n]==0){
    		cout<<"NO"<<endl;
    		return 0;
    	}
    	solve(1,n);
    	cout<<"YES"<<endl;
    	
    	return 0;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值