Educational Codeforces Round 136 (Rated for Div. 2)(A~D)

A. Immobile Knight

给出一个n*m的棋盘,如果存在一个点使得马可以向任何方向走,输出满足条件的任意一点;否则输出棋盘上任意一点。

思路:必须满足长大于3,宽大于2才存在满足条件的点,该种条件下输出(m+1)/2,(n+1)/2,否则任意输出一点。

AC Code:

#include <bits/stdc++.h>

typedef long long ll;
const int N=105;
int t;

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin>>t;
	while(t--){
		int n,m;
		std::cin>>n>>m;
		int a=std::max(n,m);
		int b=std::min(n,m);
		if(a>3&&b>2) std::cout<<1<<' '<<1<<'\n';
		else std::cout<<(n+1)/2<<' '<<(m+1)/2<<'\n';
	}
	return 0;
}

B. Array Recovery

给出一个数组a,构造一个数组d,满足d[1]=a[1],d[i]=abs(a[i]-a[i-1]),如果这个数组d是唯一的,构造出这个数组,否则输出-1。

思路:明显考差前缀和。对于a求前缀和数组,对于前缀和数组中的每个元素dif[i],如果它大于a[i+1],那说明下一个数可以是dif[i]+a[i+1],也可以是dif[i]-a[i+1],即不唯一,则输出-1。

AC Code:

#include <bits/stdc++.h>

typedef long long ll;
const int N=105;
int t,n;
int a[N],dif[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++){
			std::cin>>a[i];
			dif[i]=dif[i-1]+a[i];
		}
		bool flag=true;
		for(int i=1;i<n;i++){
			if(dif[i]>=a[i+1]&&a[i+1]){
				flag=false;
				break;
			}
		}
		if(!flag){
			std::cout<<-1<<'\n';
			continue;
		}
		for(int i=1;i<=n;i++){
			std::cout<<dif[i]<<" \n"[i==n];
		}
	}
	return 0;
}

C. Card Game

给出偶数n张牌,每张牌上写着1~n中一个数字且不重复,将这些牌分给A和B两人,每一轮先手出牌,下一个人出牌的数字要大于前一个,否则输,每一轮互换先手,问分牌的方案数,使得:A赢,B赢,平局。(平局是指按照游戏规则出牌,最后两人正好出完所有手中牌)

思路:首先平局为1,其他情况必存在谁赢谁输的情况,所以我们可以先计算先手必胜,用全部的情况减去先手必胜和平局的这一场。考虑先手必胜时,这个题可以分成两个一组和四个一组的情况,这里我采用的是四个一组的情况。先考虑有八张牌的时候,对于5 6 7 8四张牌的分配来说,如果最大牌在A手中,则先手必胜,这样的话其他的牌可以随机分开,用组合数计算很容易;如果567在A手中,则只需要再在1~4选择一张牌;如果在5~8四张牌中打成平局,即A拿到67两张牌,那该种情况下先手必胜的方案数等于在四张牌局面下的方案数,即1~4四张牌时。所以我们只需要初始化在2张牌和4张牌时的方案数,递推得到答案。

AC Code:

#include <bits/stdc++.h>
 
#define int long long
typedef long long ll;
const int mod=998244353;
const int N=105;
int t,n;
int fact[N],infact[N],f[N];
 
int pmod(int a,int b){
	int res=1;
	while(b){
		if(b&1) res=res*a%mod;
		b>>=1;
		a=a*a%mod;
	}
	return res;
}
 
void init(){
	fact[0]=infact[0]=1;
	for(int i=1;i<=N-3;i++){
		fact[i]=fact[i-1]*i%mod;
		infact[i]=infact[i-1]*pmod(i,mod-2)%mod;
	}
}
 
int C(int n,int x){
    return fact[n]*infact[n-x]%mod*infact[x]%mod;
}
 
signed main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    init();
    f[2]=1,f[4]=3;
    for(int i=6;i<=60;i+=2){
    	f[i]=(C(i-1,i/2)+C(i-4,i/2-3)+f[i-4])%mod;
    }
    std::cin>>t;
    while(t--){
    	std::cin>>n;
    	int sum=C(n,n/2);
    	int a=f[n];
    	int b=((sum-a-1)%mod+mod)%mod;
    	int c=1;
    	std::cout<<a<<' '<<b<<' '<<c<<'\n';
    }
    return 0;
}

os:一到组合数学就爆炸了,更何况这个还有博弈呜呜呜

D. Reset K Edges

给出一棵树,以1为根节点,可以进行k次操作,每次可以删除一条边,将子节点连到根节点上,问经过k次操作,得到最小的树高是多少。

思路:显然二分(bushi,对于每次操作,一定是删除最深的点最优,所以二分的是最小的树深,可以在每次check时对于mid深度以下的叶节点都删掉。具体见代码。

AC Code:

#include <bits/stdc++.h>
 
typedef long long ll;
const int N=2e5+5;
int t,n,k,idx,cnt;
int e[N<<1],next[N<<1],head[N<<1],fa[N];

void add_edge(int u,int v){
	e[idx]=v;
	next[idx]=head[u];
	head[u]=idx++;
}

int DFS(int u,int father,int mid){
	int max=0;
	for(int i=head[u];~i;i=next[i]){
		int j=e[i];
		if(j==father) continue;
		max=std::max(max,DFS(j,u,mid));
	}
	max++;
	if(father==1||u==1) return 0;
	if(max>=mid){
		cnt++;
		return 0;
	}
	else return max;
}

bool check(int mid){
	cnt=0;
	DFS(1,0,mid);
	return cnt<=k;
}
 
int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    std::cin>>t;
    while(t--){
    	std::cin>>n>>k;
    	idx=0;
    	// memset(head,-1,sizeof(head));
    	for(int i=1;i<=n;i++){
    		head[i]=-1;
    	}
    	for(int i=2;i<=n;i++){
    		int p;
    		std::cin>>p;
    		fa[i]=p;
    		add_edge(i,p),add_edge(p,i);
    	}
    	int l=1,r=n;
    	while(l<r){
    		int mid=l+r>>1;
    		if(check(mid)) r=mid;
    		else l=mid+1;
    	}
    	std::cout<<l<<'\n';
    }
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值