写在前面:
这是昨天夜晚的一场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;
}