训练总结(A~E)Codeforces Round #761 (Div. 2)

写在前面:
这是昨天夜晚的一场div2,会写的都写了,虽然不是特别快,但是没有在一个地方卡很久。写完D1还有四十分钟,这个时间并不足以攻克一个比较难的题,所以解题速度还是有很大的提升空间的。

英文题解:传送门

C. Paprika and Permutation

上次牛客训练赛出了一个 set 题,从那以后我就对 set 爱不释手了。multiset 有一个需要注意的点就是删除一个元素最好删迭代器,否则会把该类元素都删了。
set和multiset都支持find、lower_bound、erase、end、begin、insert,有了这几大功能,已经和随机访问没有多大的区别了(不知道比priority_queue强了多少倍)。

代码:

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;

multiset<int>st;
void solve()
{
    st.clear();
    int n,val;cin>>n;
    for(int i=1;i<=n;i++){
        cin>>val;
        st.insert(val);
    }
    int ans=0;
    for(int i=n;i>=1;i--){
        if(st.count(i))
        {
            auto j=st.find(i);
            st.erase(j);
            continue;
        }
        else
        {
            auto j=st.end();
            j--;
            val=*j;
            if(val&1)
            {
                if(val/2>=i) ans++;
                else
                {
                    cout<<"-1"<<endl;
                    return;
                }
            }
            else
            {
                if(val/2-1>=i) ans++;
                else
                {
                    cout<<"-1"<<endl;
                    return;
                }
            }
            st.erase(j);
        }
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    int t;cin>>t;
    while(t--){
        solve();
    }
    return 0;0
}

D. Too Many Impostors

拿到这个题我就在想这个3该怎么用,试着把相邻3个数一组,倒腾了一下还是不知道怎么办。然后想了一下这种情况 101101101(0表示骗子)。这是一种让询问结果全部为1的极端情况,但是这是不可能出现的,因为
骗子数 > n / 3 >n/3 >n/3。所以,一定会存在类似于 1010这种情况,左边三个结果和右边三个结果不同。我们找到了一对01,利用它我们能够轻易判断所有数的值。
想到这里还不足以解决hard版本,因为hard版本只能问 n + 6 n+6 n+6次。
题解的思路就很妙,它分了 n / 3 n/3 n/3个组,用了 n / 3 + 2 n/3+2 n/3+2做了一个预处理,而且顺带找到了一个01对。利用这个01对,对预处理过的每个组询问2次(2换3),得出了所有数的值。
n n n个数分成 n / 3 n/3 n/3个组,由于0的个数多于 n / 3 n/3 n/3, 1的个数也多于n/3,所以必有一组至少2个1,必有1组至少2个0,所以必有相邻的值不同的组。这两个组相邻,但是他们是不相交的,和第一版的情形不同。但是,这其实还是等价的,因为从0到1必有一个过渡的过程,所以, ( i , i + 1 , i + 2 ) , ( i + 1 , i + 2 , i + 3 ) , ( i + 2 , i + 3 , i + 4 ) , ( i + 3 , i + 4 , i + 5 ) (i,i+1,i+2), (i+1,i+2,i+3), (i+2,i+3,i+4), (i+3,i+4,i+5) (i,i+1,i+2),(i+1,i+2,i+3),(i+2,i+3,i+4),(i+3,i+4,i+5),这四组数比有相邻的两组值不同,这样我们就在 n / 3 + 2 n/3+2 n/3+2步内找到了一个01对。

代码:

#include<bits/stdc++.h>
//#define endl '\n'
using namespace std;

int ans[10010],v[10010];
void ask(int val,int x,int y){
	cout<<"? "<<val<<" "<<x<<" "<<y<<endl;
	cout.flush();
	cin>>ans[val];
}
void solve()
{
	int n,a,b,c;cin>>n;
	for(int i=1;i<=n;i+=3){
		a=i;b=i+1;c=i+2;
		cout<<"? "<<a<<' '<<b<<' '<<c<<endl;
		cout.flush();
		cin>>v[i];
	}
	int x,y;
	for(int i=1;i<=n;i+=3){
		int a=i,b=i+1,c=i+2,d=i+3,e=i+4,f=i+5;
		if(b>n) b-=n;if(c>n) c-=n;if(d>n) d-=n;if(e>n) e-=n ;if(f>n) f-=n;
		if(v[a]==v[d]) continue;
		cout<<"? "<<b<<' '<<c<<' '<<d<<endl;
		cout.flush();
		cin>>v[b];
		cout<<"? "<<c<<' '<<d<<' '<<e<<endl;
		cout.flush();
		cin>>v[c];
		if(v[a]!=v[b])
		{
			ans[a]=v[a];
			ans[d]=v[b];
			x=a;y=d;
		}
		else  if(v[b]!=v[c])
		{
			ans[b]=v[b];
			ans[e]=v[c];
			x=b;y=e;
		}
		else if(v[c]!=v[d])
		{
			ans[c]=v[c];
			ans[f]=v[d];
			x=c;y=f;
		}
		break;
	}
	if(ans[x]<ans[y]) swap(x,y);
	for(int i=1;i<=n;i+=3){
		int a=i,b=i+1,c=i+2;
		if(a==x or a==y)
		{
			ask(b,x,y);
			ask(c,x,y);
			continue;
		}
		else if(b==x or b==y)
		{
			ask(a,x,y);
			ask(c,x,y);
			continue;
		}
		else if(c==x or c==y)
		{
			ask(b,x,y);
			ask(a,x,y);
			continue;
		}
		if(v[a]==1)
		{
			cout<<"? "<<a<<' '<<b<<' '<<y<<endl;
			cout.flush();
			int flag;cin>>flag;
			if(flag==1)
			{
				ans[a]=ans[b]=1;
				ask(c,x,y);
			}
			else
			{
				ans[c]=1;
				ask(a,x,y);
				ans[b]=1^ans[a];
			}
		}
		else if(v[a]==0)
		{
			cout<<"? "<<a<<' '<<b<<' '<<x<<endl;
			cout.flush();
			int flag;cin>>flag;
			if(flag==0)
			{
				ans[a]=ans[b]=0;
				ask(c,x,y);
			}
			else
			{
				ans[c]=0;
				ask(a,x,y);
				ans[b]=1^ans[a];
			}			
		}
	}
	int tot=0;
	for(int i=1;i<=n;i++){
		if(ans[i]==0) tot++;
	}
	cout<<"! ";
	cout<<tot<<' ';
	for(int i=1;i<=n;i++){
		if(ans[i]==0) cout<<i<<' ';
	}
	cout<<endl;
	cout.flush();
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int t;cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

E. Christmas Chocolates

很多人和我一样会去想怎么把a在最短步内变到b,这个方向不对,但是也许我们在探索的过程中会得到一些意外的收获,比如说你发现了
a + b = 2 k , a > b a+b=2^k, a>b a+b=2k,a>b, 则k、b都是唯一的。当然,这很难。
通过题解的提示我们知道有若干个点位于一棵巨大的树上,我们要求这几个点的直径。显然,树上求直径的方法在这里是适用的,任取一个点,找到离他最远的点,再求出离当前点最远的点,直径就得到了。
在这里,距离怎么求呢?既然这颗树高度比较小,我们不妨来个暴力一点且不失优雅的方法:

int getdis(int x,int y){
    if(x>y) swap(x,y);
    if(x==y) return 0;
    return 1+getdis(x,cal(y));
}

代码:

#include<bits/stdc++.h>
//#define endl '\n'
using namespace std;

int a[200010];
int cal(int x){
    int res=1;
    while(res<x) res<<=1;
    return res-x;
}
int getdis(int x,int y){
    if(x>y) swap(x,y);
    if(x==y) return 0;
    return 1+getdis(x,cal(y));
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);

    int n;cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int mx=0,p,p2;
    for(int i=1;i<n;i++){
        int val=getdis(a[i],a[n]);
        if(val>mx)
        {
            mx=val;
            p=i;
        }
    }
    mx=0;
    for(int i=1;i<=n;i++){
        if(i==p) continue;
        int val=getdis(a[i],a[p]);
        if(val>mx)
        {
            mx=val;
            p2=i;
        }
    }
    cout<<p<<' '<<p2<<' '<<mx<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

组合,我有特殊的计数技巧

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

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

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

打赏作者

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

抵扣说明:

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

余额充值