Educational Codeforces Round 84 (Rated for Div. 2) A~E题解

A.Sum of Odd Integers

题意:
给出t组样例,每组样例给出两个正整数n,k.
问n能否是k个不同的正奇数的和.

题解:
首先判断k个奇数的最小和是否<=n,如果大于的话那么肯定没有解
k个奇数的最小和即:1,3,5,…
然后直接判断n与k的奇偶性是否相等即可.
假如n是奇数,那么k只能是奇数,因为假如k是偶数,k个奇数的和一定是偶数,此时肯定无解.
反之亦然.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,t,m;
void solve(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		if(1ll*m*m>n){
			printf("NO\n");
			continue;
		}
		if((n&1)&&(m&1)) printf("YES\n");
		else if(!(n&1)&&!(m&1)) printf("YES\n");
		else printf("NO\n");
	}
}
int main(void)
{
	solve();
	return 0;
 }

B.Princesses and Princes

题意:
给出t组样例.
每组样例给出一个正整数n,表示有n个国王和公主.
然后给出n行每行先给出一个正整数k,然后跟上k个正整数,表示公主喜欢这k个国王.
现在从第一位公主开始选,每位公主要选择她喜欢的国王里序号最小的且该国王没被前面的公主选过.
问有没有公主是没有国王可选的
题解:
因为题目输入保证公主喜欢的k个国王是升序的.
那么直接在输入的时候判断当前输入的这名国王有没有被选择,记录一下即可.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5+5;
int t,n,k,num,ans1,ans2;
bool vis1[MAX],vis2[MAX],flag;
void solve(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=1;i<=n;++i) vis1[i]=vis2[i]=false;
		ans1=ans2=0;
		for(int i=1;i<=n;++i){
			flag=false;
			scanf("%d",&k);
			while(k--){
				scanf("%d",&num);
				if(!vis2[num]&&!flag)
					vis2[num]=flag=vis1[i]=true;
			}
		}
		for(int i=1;i<=n;++i){
			if(!vis1[i]) ans1=i;
			if(!vis2[i]) ans2=i;
		}
		if(ans1){
			printf("IMPROVE\n");
			printf("%d %d\n",ans1,ans2);
		}
		else printf("OPTIMAL\n");
	}
}
int main(void)
{
	solve();
	return 0;
}

C.Game with Chips

题意:
有一个nm的矩阵,现在有k个点,给出这k个点的起点和终点.每次你可以将所有的点向L,U,R,D四个方向移动一个单位.输出一个操作序列,让所有的点都经过了对应的终点至少一次 点可以重叠. 点在边界时,如果该点是向边界的方向移动,那么该点不会改变位置.
题解:
题目没有说是最小化次数,只需要将操作次数限制在2
nm内即可.
那么直接假设点在右下角(n,m),然后先左移m,再上移n,这样就将所有的点汇集在(1,1)这个点.
然后直接S型走遍整个矩阵即可,这样就可以保证每个点都能经过对应的终点至少一次.而且这样移动的次数保证会<2
n*m

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k,num;
char op='R';
void solve(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=k;++i) scanf("%d%d",&num,&num);
	for(int i=1;i<=k;++i) scanf("%d%d",&num,&num);
	printf("%d\n",n+m+n*m-1);
	for(int i=1;i<=m;++i) printf("L");
	for(int i=1;i<=n;++i) printf("U");
	for(int i=1;i<=n;++i){
		for(int j=1;j<m;++j) printf("%c",op);
		if(i!=n)printf("D");
		op=op=='R'?'L':'R';
	}
}
int main(void)
{
	solve();
	return 0;
}

D.Infinite Path

题意:
给定一个n,然后给出n的一个排列,然后给出一个c数组,c[i]表示i的颜色为c[i]
定义 i k i^k ik为p[p[p…[i]…]] (合计k个p).
求一个最小 k k k使得p数组变为 p k p^k pk数组,即
p k p^k pk[1], p k p^k pk[2], p k p^k pk[3],…, p k p^k pk[n].
在这个数组中能找到任意一个无限序列形如i, p k p^k pk[i], p k p^k pk[ p k p^k pk[i]]…,这个序列中的所有点的颜色相同.
输出这个最小k
题解:
首先这个无限序列的形式必定是一个环.
然后观察 p k p^k pk,可以发现其实就是i指向的点在环上沿着环的方向平移了1位.
我们考虑这样一个排列 2,3,5,1,6,4
这个排列的环是2,3,5,6,4,1
他经过一次変换后是3,5,6,2,4,1
这个排列的环是3,6,1和5,4,2
我们再来整理一下.
第一个环可以写成1,2,3,5,6,4
第二个排列的环可以写成1,3,6和2,5,4
显然可以发现1,3,6是1沿着环的方向平移了一位后形成的.
至此,思路已经呼之欲出了,我们只要处理出最初p排列的所有的环,然后枚举环的长度的因数,因为假如不是环的长度的因数,那么便没有意义,如上,假如1是平移了5位,那么环就会变成1,4,6,5,3,2,这时环的长度和里面的点并没有变,只是顺序变了,这并不影响答案.枚举因数后就可以求出这个环后面会生成多少不同的环.转换的次数就是此时平移的位数,求个min即可.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 2e5+5;
const int INF = 0x3f3f3f3f;
int t,n,p[MAX],c[MAX],tot,ans;
vector<int> list1[MAX];
bool vis[MAX];
void dfs(int now){
	list1[tot].push_back(now);
	vis[now]=true;
	if(!vis[p[now]]) dfs(p[now]);
}
void solve(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n); ans=INF;
		for(int i=0;i<=n;++i) vis[i]=false,tot=0;
		for(int i=1;i<=n;++i) scanf("%d",&p[i]);
		for(int i=1;i<=n;++i) scanf("%d",&c[i]);
		for(int i=1;i<=n;++i)
			if(!vis[p[i]]){ list1[tot].clear();dfs(p[i]);++tot; }
		for(int i=0;i<tot;++i){
			int len = list1[i].size();
			for(int j=1;j<=len;++j){
				if(len%j==0){
					bool flag1 = false;
					int cnt = len/j;
					for(int s=0;s<j;++s){
						bool flag=true;
						int index=s,last=-1;
						for(int k=0;k<cnt;++k,(index+=j)%=len){
							if(last==-1) last = c[list1[i][index]];
							else if(last!=c[list1[i][index]]){ flag=false;break; }
						}
						if(flag){ ans=min(ans,j);flag1=true;break; }
					}
					if(flag1) break;
				}
			}
		}
		printf("%d\n",ans);
	}
}
int main(void)
{
	solve();
	return 0;
 } 

E. Count The Blocks

题意:求0~ 1 0 n − 1 10^{n-1} 10n1中连续数字长度分别为1 ~ n的块的数目
题解:
1.假如该块在数字的边界位置,i代表块的长度.
那么总数为10 * 9 * 2 * 1 0 n − i − 1 10^{n-i-1} 10ni1.10代表该块有10种情况.92代表该块的旁边一位数字有9种不同的情况,左右两边都有,然后还剩下n-i-1位,每一位有10种情况
2.假如该块在数字的中间位置
那么总数为10 * 9 * 9 * (n-i-1) * 1 0 n − i − 2 10^{n-i-2} 10ni2.
10代表该块有10种情况.9
9代表该块旁边两位各有9种情况相乘.
n-i-1代表该块可以放在中间的n-i-1个位置,

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5+5;
const ll mod = 998244353;
ll quick(ll a,ll n){
	ll ans=1;
	while(n>0){
		if(n&1) ans=ans*a%mod;
		n>>=1;
		a=a*a%mod;
	}
	return ans;
}
void solve() {
	int n;
	scanf("%d",&n);
	for(int i=1; i<=n; i++) {
		if(i==n) { printf("10\n");continue; }
		ll ans=0;
		(ans+=1ll*10*9*2*quick(10,n-i-1))%=mod;
		(ans+=1ll*10*9*9*(n-i-1)*quick(10,n-i-2))%=mod;
		printf("%lld ",ans);
	}
}
int main() {
	solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值