Codeforces Round 843 (Div. 2) VP题解

B. Gardener and the Array

大意:
给你一个序列,你要判断能否找到两个不同的子序列使得他们或起来相等。题目会给出每个数二进制下哪些位为 1,保证输入量不超过 1e5。

思路:

显然,如果某一些数对应的序列是另一些数的子序列,就肯定可以。我们不妨直接找最大的序列,然后对于每一个数检查一下它在这个序列里是否有独有的贡献。如果没有,就可以构造。

code

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
ll n;
vector<ll> vt[N];
map<ll,ll> mp;
void solve() 
{
    mp.clear();
    cin>>n;
    for(int i=1;i<=n;++i) vt[i].clear(); 
    for(int i=1;i<=n;++i)
	{
		ll m;
		cin>>m;
		for(int j=1;j<=m;++j)
		{
			ll a;
			cin>>a;
			vt[i].push_back(a);
			mp[a]++;	
		}	
	} 
	for(int i=1;i<=n;++i)
	{
		bool fl=1;
		for(auto j:vt[i])
		{
			if(mp[j]==1) fl=0;
		}
		if(fl)
		{
			cout<<"Yes"<<endl;
			return;
		}
	}
	cout<<"No"<<endl;
}

int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll t;cin>>t;while(t--)
	solve();
	return 0;
}

C. Interesting Sequence

大意:

给你两个数 n,x,求最小的 m 使得 n&(n+1)&(n+2)&…&m=x。无解输出-1

思路:

显然是要一位一位去check

我们假设在第i位,n对应的数字是a,b对应的数字是b

若a=0且b=1,显然无解,与操作不能无中生有

若a=1且b=0,  我们的m需要满足:第i位为0,且m>=n,这里我们就可以更新下限

若a=1且b=1, 则从n到m的每一个数字的第i位都要等于1,这里我们可以更新上限

那么最后看一下上下限是不是合法就可以了

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
ll n,m;
void solve()
{
	cin>>n>>m;
	ll l=n,r=5e18;
	for(int i=0;i<60;++i)
	{
		ll a=(n>>i)&1;ll b=(m>>i)&1;
		if(a==0&&b==1)
		{
			cout<<-1<<endl;
			return;
		}
		ll tmp=((n/(1ll<<i)+1)<<i);
		if(a==1&&b==0)
		{
			l=max(l,tmp);
			continue;
		}
		if(a==1&&b==1)
		{
			r=min(r,tmp-1);	
		}
	}
	if(l<=r)
	{
		cout<<l<<endl;
	}
	else cout<<-1<<endl;
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll t;cin>>t;while(t--)
	solve();
	return 0;
 } 

D. Friendly Spiders

大意:

给你 n个数,若 gcd(ai,aj)≠1则在 i,j之间连一条无向边,求从 s 到 t 的最短路。

思路:
思路很明显,就是对每一个点向自己的因子连边,然后跑最短路。优化一下,只连质因子。再优化一下,用一手欧拉筛,就好了。某人边数没算对,T烂了,我不说是谁

code

#include<bits/stdc++.h>
using namespace std;
#define ll int
#define endl '\n'
const ll N=3e5+10;
ll n,m;
ll A,B;
ll mas[N];
ll dis[N<<1];
ll vis[N<<1];
ll pre[N<<1];
vector<ll> ans;
struct ty
{
	ll t,l,next;
}edge[10000005];
ll cn=0;
ll head[N<<1];
void add(ll a,ll b,ll c)
{
	edge[++cn].t=b;
	edge[cn].l=c;
	edge[cn].next=head[a];
	head[a]=cn;	
}
void bfs()
{
//	memset(dis,-1,sizeof dis);
	deque<int> q; 
    q.push_back (A);
    dis[A] = 1;
    while (!q.empty ()) {
        auto u = q.front ();
        q.pop_front ();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v = edge[i].t, dist = edge[i].l;
            if (dis[v])     continue;
            if (dist == 1)  q.push_back (v); //1尾
            else    q.push_front (v); //0首
            dis[v] = dis[u] + dist;
            pre[v] = u; //记录前驱, 输出路径
        }
    }
}
void solve()
{
	memset(head,-1,sizeof head);
	cin>>n;
	for(int i=1;i<=n;++i) cin>>mas[i];
	cin>>A>>B;
	for(int i=1;i<=n;++i)
	{
		for(int j=2;j*j<=mas[i];++j)
		{
			if(mas[i]%j) continue;
			while(mas[i]%j==0) mas[i]/=j;
			//cout<<i<<' '<<j<<endl;
			add(i,n+j,0);
			add(n+j,i,1);
		}
		if(mas[i]!=1) 
		{
			//cout<<i<<" "<<mas[i]<<endl;
			add(i,n+mas[i],0);
			add(n+mas[i],i,1);
		}
	}
	bfs();
	if(dis[B]==0)
	{
		cout<<-1<<endl;
		return;
	}
	cout<<dis[B]<<endl; 
	ll pos=B;
	
	while(pos!=A)
	{
		ans.push_back(pos);
		pos=pre[pos];
		pos=pre[pos];
	}
	reverse(ans.begin(),ans.end());
	cout<<A<<' ';
	for(auto i:ans) cout<<i<<" ";
	cout<<endl;
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//	ll t;cin>>t;while(t--)
	solve();
	return 0;
 } 

E. The Human Equation

大意:

给你一个序列,你可以执行任意次以下操作:

选出一个子序列,将奇数位 +1,偶数位 −1,或将奇数位 −1,偶数位 +1。

问最少几次操作使得序列变成全 0

思路:
纯抽象,cf的思维题我一向是很认可的

显然我们会尽可能去扩展每一次的操作范围。对于一个正数来说,如果某一次操作能让他--,我们肯定会做,如果是让他++,我们肯定不会做,这是显然的,因为这里不会有比直接减更好的策略,毕竟我们每次操作改变的值是固定的。

然后,我们从前往后看。如果当前的数字a大于0,我们至少要a次--操作,那么我们就留给了后面的第一个数字a次++操作,同时也会消耗掉之前的数字留给我们的a次--操作。然后如果a小于0,我们需要消耗|a|次++操作,留给后面|a|次--操作。那么在这个过程中,如果需要的次数不够了,我们就直接加,因为这就是我们不得不额外做的操作。否则就用之前的操作传下来的就好了

是不是很玄学

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=3e5+10;
ll n,m;
ll mas[N];
void solve()
{
	cin>>n;
	for(int i=1;i<=n;++i) cin>>mas[i];
	ll cn1=0,cn2=0;
	for(int i=1;i<=n;++i)
	{
		if(mas[i]>0)
		{
			cn2+=mas[i];
			cn1=max(0ll,cn1-mas[i]);
		}
		else if(mas[i]<0)
		{
			cn1-=mas[i];
			cn2=max(0ll,cn2+mas[i]);
		}
	}
	cout<<cn1+cn2<<endl;
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll t;cin>>t;while(t--)
	solve();
	return 0;
 } 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值