又是一场speedforces ,可恶的棒子。但是好在b题卡了一会后,c,d 都切的很快。把前两场的分上回来了。
A: 分组不影响 mex 的值,所以直接求解 mex 输出即可
B: f(i) 本质是每个后缀,不同数字的个数。可以观察到每增加一个与之前的数字不同的数,贡献是i,所以如果贡献j不足i,说明num[i]=num[i-j]。因为数据保证一定有合法生成,所以不用考虑不存在的情况。
所以对于给定的数组a。
num[i]=num[i-j] ,a[i]-a[i-1]!=i
num[i]=i ,a[i]-a[i-1]==i
inline void solve()
{int n;cin>>n;vector<int> a(n+1);for(int i=0;i<n;i++){cin>>a[i];}vector<int> num(n+1);num[0]=1;for(int i=1;i<n;i++){int d=a[i]-a[i-1];if(d==i+1){num[i]=i+1;}else{num[i]=num[i-d];}} for(int i=0;i<n;i++) cout<<num[i]<<" ";cout<<endl;
}
C: 经过一系列观察,发现可以的情况为,数字是一个二进制回文数字(如果长度为奇数,那么中间位不能是1)。但是你可以通过在数字前面补前导0来构成回文。因为数据范围 \(<2^{30}\) 所以最多在数字前面加30次前导0,其中有任意一次符合条件, 即为"YES" ,否则为"NO" 。
inline bool check(string s)
{if(s.size()%2){if(s[s.size()/2]=='1') return 0;}for(int i=0;i<s.size()/2;i++){if(s[i]!=s[s.size()-1-i]) return 0;}return 1;
}
inline void solve()
{int n;cin>>n;if(n==0){cout<<"YEs"<<endl;return;}string s;while(n){s+='0'+(n&1);n>>=1;}for(int i=0;i<=30;i++){string st=s;for(int j=1;j<=i;j++) st+='0';if(check(st)){cout<<"YEs"<<endl;return;}}cout<<"NO"<<endl;
}
D: 很简单的一道交互?
按顺序依次将编号放入 查询数组中,如果查询结果为0,则继续;否则该位即为,查询所得的数字,并将该位弹出。遍历第一遍,一定可以找出1-n的数字各一个。第二步,清空查询数组,把找出来的1-n个数字放入查询数组,对于每一个未知位,放入查询数组,查询一次后弹出。第二遍遍历,即可确定未知的n个数字。
int ask(vector<int> a)
{cout<<"? ";cout<<a.size()<<" ";for(int i=0;i<a.size();i++) cout<<a[i]<<" ";cout<<endl;int ans;cin>>ans;return ans;
}
inline void solve()
{int n;cin>>n;vector<int> ans(2*n+5);vector<int> a;a.push_back(1);int sum=0;for(int i=2;i<=2*n;i++){a.push_back(i);int t=ask(a);if(t==0) continue;ans[i]=t;a.pop_back();sum++;if(sum==n) break;}a.clear();for(int i=1;i<=2*n;i++){if(ans[i]!=0) a.push_back(i);}for(int i=1;i<=2*n;i++){if(ans[i]==0){a.push_back(i);ans[i]=ask(a);a.pop_back();}}cout<<"! ";for(int i=1;i<=2*n;i++){cout<<ans[i]<<" ";}cout<<endl;
}