Codeforces Round #554 (Div. 2) BCDE 题解

好久没写 cf 题解了,决定今天要勤奋一下下.....

B. Neko Performs Cat Furrier Transform

//感觉前4题B题最难

题意:给定一个数x(x<=1e6),你有最多不超过40次的偶数次操作,使得x对应的二进制数每位都是1,对于第 i 次操作,如果 i 为奇数,那么你可以选择一个n,使得x=x^(2^n-1),如果 i 为偶数,那么x=x+1。

思路:我们从高位开始到低位枚举每个0,使用奇数次操作把它置1就好了,但是因为第偶数次操作必须使得x++可能会有bug,比如1000,把最高的0置1,就变成了1111,那么接下来的操作就变成了10000,那么这种情况我们特判一下,我们先让它异或0,然后+1变成10001,然后再开始把最高位的0置1:11110,然后+1就求出答案了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int q[30],cnt;
int main()
{
	int x,y;
	cin>>x;
	y=x;
	for(int i=19;i>=0;i--)
	if(!(x&(1<<i)))
	{
		q[++cnt]=i+1;
		x^=((1<<i+1)-1);
		y=y^((1<<i+1)-1);
		y++;			
	}
	int all=(1<<20)-1;
	if(y!=all)
	{
		y--;
		y^=((1<<q[cnt])-1);
		int tmp=q[cnt];
		y++;
		q[cnt]=0;
		y^=((1<<tmp)-1);
		y++;
		q[++cnt]=tmp;
	}
	printf("%d\n",cnt*2);
	for(int i=1;i<=cnt;i++)
	printf("%d ",q[i]);
	
}

C. Neko does Maths

题意:给a,b,求一个k(k>=0),使得lcm(a+k ,b+k)最小。

思路:设d = abs(n-m),我们枚举d的因数x,假设 x 是(n+k),(m+k)的gcd,然后求出他们的lcm看是否是最小即可,很明显的一点,k=(n-n%x)%n,当n%x != m%x 时 k 无解。(很多时候(n+k),(m+k)的gcd不为x,但是不影响答案,想一想,为什么)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
void update(ll& ans,int& k,ll n,ll m,int d)
{
	if(n%d!=m%d)return;
	int tmp=0;
	if(n%d)tmp+=(d-n%d);
	n+=tmp;
	m+=tmp;
	ll res=n/d*m;
	if(res<ans)
	ans=res,k=tmp;
}
int main()
{
	ll n,m;
	cin>>n>>m;
	if(n<m)swap(n,m);
	ll ans=n*m/__gcd(n,m);
	int k=0;
	if(n%m==0)puts("0");
	else
	{
		int d=n-m;
		for(int i=1;i*i<=d;i++)
		if(d%i==0)
		{
			update(ans,k,n,m,i);
			update(ans,k,n,m,d/i);
		}
		cout<<k;
	}
}

D. Neko and Aki's Prank

题意:有一颗深度为2*n的字典树(假设初始根深度为0),每个节点要么存 '(' 要么存 ')',规定根到每个叶子节点路径的字符串必须是平衡括号串,现在你要给一些边涂色,要求不能有一个节点连接多条涂色的边,求最多可以涂多少条边。

思路:画一颗深度为 2*n 的树,分析一下很容易发现答案是奇数深度节点的个数,但是这颗树的节点个数和 n 是指数函数关系,所以只能用dp来求了,设d[ i ][ j ]为从根节点到深度为 i 的节点中,左括号 - 右括号 = j 的节点个数(注意不要产生非法串比如"()())" ),状态转移很简单,我可以在当前节点加一个左括号,那么d[ i+1 ][ j+1 ]+=d[ i ][ j ],我也可以在当前节点加一个右括号,那么d[ i ][ j-1 ]+=d[ i ][ j ]。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e3+10,mod=1e9+7;
int d[maxn*2][maxn]; 
void add(int &x,int y)
{
	x=x-mod+y;
	if(x<0)x+=mod;
}
int main()
{
	int n,ans=0;
	scanf("%d",&n);
	d[0][0]=1;
	for(int i=1;i<=2*n;i++)
	{
		for(int j=0;j<i;j++)
		{
			if(2*n-i>=j+1)
			add(d[i][j+1],d[i-1][j]);
			if(j>0&&(2*n-i>=j-1))
			add(d[i][j-1],d[i-1][j]);
		}
		if(i%2) 
		for(int j=0;j<=i&&j<=(2*n-i);j++)
		add(ans,d[i][j]);
	}
	cout<<ans;
}

E. Neko and Flashback

题意:有一个长度为 n 的 a 数组,长度为 n-1 的排列 p ,定义 b , c 数组:bi = min( ai, ai+1 ),ci = max( ai, ai+1 ),定义 b′ ,c' 数组:        b' = bpi,c' = cpi,现只给出 b' , c' 数组,求 a 数组。

思路:对于每个b'i,c'i,我们都知道了在a数组中他们是相邻的,那我们不妨不所有的b'i ,c'i 连接起来,然后跑一遍欧拉路,如果存在合法欧拉路且所有b'i < c'i ,那么欧拉路遍历的点就可以是a数组。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int arr[maxn*2],b[maxn],c[maxn],d[maxn],sz;
map<int,int>G[maxn];
stack<int>s;
void dfs(int u)
{
	map<int,int>::iterator it;
	for(it=G[u].begin();it!=G[u].end();it++)
	{
		int v=it->first;
		if(it->second <= 0)
		continue;
		G[u][v]--;
		G[v][u]--;
		dfs(v);
	}
	s.push(arr[u]); 
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	scanf("%d",&b[i]),arr[++sz]=b[i];
	for(int i=1;i<n;i++)
	scanf("%d",&c[i]),arr[++sz]=c[i];
	sort(arr+1,arr+1+sz);
	sz=unique(arr+1,arr+1+sz)-arr-1;
	int flag=1;
	for(int i=1;i<n;i++)
	{
		if(b[i]>c[i])flag=0;
		b[i]=lower_bound(arr+1,arr+sz+1,b[i])-arr;
		c[i]=lower_bound(arr+1,arr+sz+1,c[i])-arr;
		d[b[i]]++;
		d[c[i]]++;
		G[b[i]][c[i]]++;
		G[c[i]][b[i]]++;
	}
	int rt=1,res=0;
	for(int i=1;i<=sz;i++)
	if(d[i]%2)rt=i,res++;
	dfs(rt);
	if(s.size()!=n||!flag||sz>n||res==1||res>2)puts("-1");
	else
	{
		while(!s.empty())
		printf("%d ",s.top()),s.pop();
	}
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值