D.Rocket
题意:
交互题
给定m和n,要求你猜一个x,x的范围在1到m内
给机器人y,如果x<y则返回-1,如果x>y则返回1,如果x=y则返回0,
但是机器人有故障,有可能返回错误的答案
机器人内部有一个长度为n的序列p,p(i)只有0和1两种取值,玩家是不知道p中的元素值的。
第i次问机器人的时候,假设应该返回的答案是t,如果p(i)=1,那么机器人会回答-t,如果p(i)=0,那么机器人会回答t
现在要你在60次询问之内,问出x的值。
数据范围:m<=1e9,n<=30
解法:
如果没有真假话系统的话,显然可以直接二分。
考虑到当我们问机器人1的时候:
1.如果x=1,那么机器人会返回0,0的时候一定是真话,因此答案就出来了
2.如果x!=1,因为x是在1到m范围内的,那么x一定大于1,
这时候如果机器人返回1,表明这时候的是真话,p(i)=1,反之如果返回-1则p(i)=0
这样我们就能用n次测试测数p数组,然后二分就行了(用p数组来获得正确回答)。
因为230大于1e9,因此一定能在30次之内测出来
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=35;
int p[maxm];
signed main(){
int m,n;
cin>>m>>n;
int x;
for(int i=0;i<n;i++){
cout<<1<<endl;
cin>>x;
if(x==1)p[i]=1;
else p[i]=0;
}
int l=2,r=m;
int now=0;
while(x!=0){
int mid=(l+r)/2;
cout<<mid<<endl;
cin>>x;
if(!p[now++])x=-x;
now%=n;//记得取模
if(x==1)l=mid+1;
else r=mid-1;
}
return 0;
}
E.Border
题意:
给一个长度为n的数组a,和一个整数k。
你可以选择数组中的任意数相加(每个数无限个,可任选),问相加的结果k进制最后一位有多少种情况。
输出所有情况。
解法:
裴蜀定理(贝祖定理):若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。
它的一个重要推论是:a,b互质的充要条件是存在整数x,y使ax+by=1.
n个整数间的裴蜀定理:设a1,a2,a3…an为n个整数,d是它们的最大公约数,
那么存在整数x1…xn使得x1a1+x2a2+…xnan=d。
特别来说,如果a1…an互质(不是两两互质),那么存在整数x1…xn使得x1a1+x2a2+…xnan=1。
这道题的解法:
假设第i个数用xi次,这些数相加就是式子x1a1+x2a2+…xnan,
由裴蜀定理可知:设a1,a2,a3…an的最大公约数为d,那么上面的式子结果一定是d的倍数,
因为是在模k意义下,因此计算出d,在0到k-1范围内枚举倍数即可(k对k取模就变成0了,因此只枚举到k-1)
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
signed main(){
int n,k;
cin>>n>>k;
int gg=0;
for(int i=1;i<=n;i++){
int x;
cin>>x;
gg=gcd(gg,x);
}
gg%=k;
set<int>ans;
for(int i=0;i<k;i++){
ans.insert(gg*i%k);
}
cout<<ans.size()<<endl;
for(int v:ans)cout<<v<<' ';
return 0;
}
F.Mars rover
题面:
题意:
给一颗树,1为根,每个节点最多两个子节点,每个非叶子点有与、或、非、异或,这4种操作的其中一种(如图),
叶子节点有一个一比特的初值0或者1,然后不断向上按节点操作传递到根,从根节点输出数据。
现在问每个叶子节点如果初值改变,那么根节点的数据是多少,每次叶子节点改变是独立的。
输出每个叶子节点改变后根结点的输出。
思路:
如果每次都修改叶子节点然后在从下向上上传数据,显然是不行的。
先计算出叶子不改变的情况下,每个节点的值,一次dfs就可以求出
从根开始向下检测,假如根是and,
如果根的值为1,那么左右节点改变都会影响根节点的答案,那么处理左右节点
如果根的值为0,假设左右节点为0,1,那么值为0的节点改变会影响根的答案,那么递归处理制为0的节点
其他情况类似,即只向有可能影响到根的节点进行dfs搜索,如果搜到了叶子节点,那么标明这个叶子会影响根。
这样的话一次dfs就能计算出哪些叶子节点的改变会影响根了。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e6+5;
vector<int>g[maxm];
int mark[maxm];
int d[maxm];//操作符
int v[maxm];//值
int n;
/*
and 1
or 2
xor 3
not 4
in 5
*/
int dfs(int x){//计算出初始情况每个节点的值
if(d[x]==1)return v[x]=dfs(g[x][0])&dfs(g[x][1]);
else if(d[x]==2)return v[x]=dfs(g[x][0])|dfs(g[x][1]);
else if(d[x]==3)return v[x]=dfs(g[x][0])^dfs(g[x][1]);
else if(d[x]==4)return v[x]=!dfs(g[x][0]);
else return v[x];
}
void dfs2(int x){
if(d[x]==1){//and
if(v[x]==1){//两个子节点改变会造成影响
dfs2(g[x][0]);
dfs2(g[x][1]);
}else{
if(!v[g[x][0]]&&v[g[x][1]])dfs2(g[x][0]);//第一个改变会有影响
if(v[g[x][0]]&&!v[g[x][1]])dfs2(g[x][1]);//第二个改变会有影响
}
}else if(d[x]==2){//or
if(v[g[x][0]]&&!v[g[x][1]])dfs2(g[x][0]);
if(!v[g[x][0]]&&v[g[x][1]])dfs2(g[x][1]);
if(!v[g[x][0]]&&!v[g[x][1]]){
dfs2(g[x][0]);
dfs2(g[x][1]);
}
}else if(d[x]==3){//xor
dfs2(g[x][0]);
dfs2(g[x][1]);
}else if(d[x]==4){//not
dfs2(g[x][0]);
}else if(d[x]==5){//in
mark[x]=1;//这个叶子改变会改变根
}
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n;
string s;
int l,r;
for(int i=1;i<=n;i++){
cin>>s;
if(s[0]=='A'){
d[i]=1;
cin>>l>>r;
g[i].push_back(l);
g[i].push_back(r);
}else if(s[0]=='O'){
d[i]=2;
cin>>l>>r;
g[i].push_back(l);
g[i].push_back(r);
}else if(s[0]=='X'){
d[i]=3;
cin>>l>>r;
g[i].push_back(l);
g[i].push_back(r);
}else if(s[0]=='N'){
d[i]=4;
cin>>l;
g[i].push_back(l);
}else if(s[0]=='I'){
d[i]=5;
cin>>v[i];
}
}
dfs(1);
dfs2(1);
for(int i=1;i<=n;i++){
if(d[i]==5){
if(mark[i])cout<<!v[1];
else cout<<v[1];
}
}
return 0;
}