Codeforces Round 1058 (Div. 2)(A-D)
题目链接:Dashboard - Codeforces Round 1058 (Div. 2) - Codeforces
A. MEX Partition
题意
给你一个数组,将其分成若干个子集,每个子集求mex,要求相等且最小
思路
个人感觉题目比较难懂,平常A差不多2分钟以内开,这个大概6分钟才开出来,(比较绕只能说自己太菜了)
如果数组种没有0的,那么无论怎么划分,所有子集都是0,输出0即可,如果有0的话就再想一下,我们始终要把下一个mex均分到所有子集不然就不能够相等
所以我们最终得出结论就是求整个数组的mex即可
代码
void solve(){int n;cin>>n;vi a(n+1);map<int,bool> mp;for(int i=1;i<=n;i++){cin>>a[i];mp[a[i]]=true;}int mex=0;while(mp[mex]) mex++;cout<<mex<<"\n";
}
B. Distinct Elements
思路
观察此式子,f(a[1,i])指的是[1,i]区间内不同数的数量,那么显然我们没法根据这个来确认a的值,我们不妨通过递推的角度观察b数组,用表示x是否在a[l,r]种出现过
显然这是必然的
,显然f(a[i,i])=1以下用1表示
那么观察一下可以得到以下结论
1.如果cal(a[l,r],x)=1,那么cal(a[L,r],x)都为1 其中L>l
2.如果cal(a[l,r],x)=0且cal(a[l+1,r],x)=1,那么x=a[l]
所以递推地从[1->n]开始填数,根据来填写a[i]的值即可
代码
#include<bits/stdc++.h>
using namespace std;#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
#define int long long
#define vi vector<int>
#define vb vector<bool>
typedef pair<int,int> pll;void solve(){int n;cin>>n;vi b(n+1);for(int i=1;i<=n;i++){cin>>b[i];}vi ans(n+1);int now=1; //用于填数ans[1]=1;for(int i=2;i<=n;i++){int cnt=b[i]-b[i-1];if(b[i]-b[i-1]==i){ans[i]=++now;}else{ans[i]=ans[i-cnt];}}for(int i=1;i<=n;i++){cout<<ans[i]<<" \n"[i==n];}
}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;cin>>_;while(_--) solve();return 0;
}
C. Reverse XOR
思路
做这种题当然要打表了!
然后你就会发现所有能够得到的n在二进制下都满足:对称结构+若干个(可为0)0
例如:1100,1111,101010100
对称的结构很明显能够得到,但0又是怎么回事,就拿1100来说其实是由110111或者111011得来的
自己模拟一下其实就明白怎么回事了
注意:如果右半边对称结构是个奇数个中间不能为1,这也很好理解吧,不然就会和我一样WA一发了(0_0)
代码
#include<bits/stdc++.h>
using namespace std;#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
#define int long long
#define vi vector<int>
#define vb vector<bool>
typedef pair<int,int> pll;const int N=2e5+10;
const int inf=1e18;
const int mod=998244353;bool check(vi& res){int n=res.size();if(n%2&&res[n/2]==1) return true;return false;
}void solve(){int n;cin>>n;while(n){if(n&1) break;n/=2;}vi res;while(n){res.push_back((n&1));n/=2;}int m=res.size();if(m==0){cout<<"YES\n";return;}if(check(res)){cout<<"NO\n";return;}for(int i=0;i<m/2;i++){if(res[i]!=res[m-i-1]){cout<<"NO\n";return;}}cout<<"YES\n";
}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;cin>>_;while(_--) solve();return 0;
}
D. MAD Interactive Problem
思路
虽然放到了D题但我个人感觉好像并不难
首先跑一遍数组,即询问2n次,每次加进i来,如果为0则放在询问序列里面,不为0则说明该位置其值为询问回复的答案。
因此我们经过2n次询问就可以将n个未确认的数和n个已经确认的数给得到了,那么剩下的只需要再询问n次,即拿已经确认的序列来不断加一个未确认的数询问一次即可知道其值
这样正好3*n次询问
代码
#include<bits/stdc++.h>
using namespace std;#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
#define int long long
#define vi vector<int>
#define vb vector<bool>
typedef pair<int,int> pll;const int N=2e5+10;
const int inf=1e18;
const int mod=998244353;int query(vi& v){cout<<"? "<<v.size()<<" ";for(int i=0;i<v.size();i++){cout<<v[i]<<" ";}cout<<endl;int res;cin>>res;return res;
}void solve(){int n;cin>>n;vi ans(2*n+1);vector<int> res; //不断维护查询为0的序列vector<int> q; //记录所有确认的位置for(int i=1;i<=2*n;i++){res.push_back(i);int x=query(res);if(x!=0){ans[i]=x;q.push_back(i);res.pop_back();}}//res里面全是未确认的for(int i=0;i<res.size();i++){q.push_back(res[i]);int x=query(q);ans[res[i]]=x;q.pop_back();}cout<<"! ";for(int i=1;i<=2*n;i++){cout<<ans[i]<<" ";}cout<<endl;
}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;cin>>_;while(_--) solve();return 0;
}