Codeforces Round #781 (Div. 2)(ABCDE)

Posted 斗奋力努

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #781 (Div. 2)(ABCDE)相关的知识,希望对你有一定的参考价值。

Codeforces Round #781 (Div. 2)(ABCDE)

A. GCD vs LCM


题意:
给定一个n,将n分成4部分,满足 a + b + c + d = n & & g c d ( a , b ) = = l c m ( c , d ) a+b+c+d=n\\&\\&gcd(a,b)==lcm(c,d) a+b+c+d=n&&gcd(a,b)==lcm(c,d)
思路:
先判断n是否为4的倍数,是的话分成4等份, a = b = c = d a=b=c=d a=b=c=d
不是的话,保证 c = d = 1 c=d=1 c=d=1,那么 l c m ( c , d ) = 1 lcm(c,d)=1 lcm(c,d)=1,剩下保证gcd为1就行了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,a,b,c,d;

void solve()
    scanf("%d",&n);
    if(n%4==0)printf("%d %d %d %d\\n",n/4,n/4,n/4,n/4);return;
    c=d=1; n-=2;
    if(n%2==1) a=n/2,b=(n+1)/2;
    else a=n/2-1,b=n/2+1;
    printf("%d %d %d %d\\n",a,b,c,d);


int main()
    int T;scanf("%d",&T);
    while(T--) solve();
    return 0;

B. Array Cloning Technique


题意:
给定长度为n的序列,最少进行多少次操作是使得有一个序列的元素全部相等。
一次操作:
①复制任意一个序列
②交换任意两个序列中的一个元素
思路:
肯定是去使初始最多的元素全部填满某个序列,如果总数不够,就去复制一次。
然后将复制出来的全部移到原序列。还不够就再复制,然后再交换,直到填满为止

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m,q;
map<int,int>mp;

void solve()
    mp.clear();
    scanf("%d",&n);
    int mx=0;
    for(int i=1;i<=n;i++)
        int x;scanf("%d",&x);
        mp[x]++; mx=max(mx,mp[x]);
    
    int ans=0;
    while(mx<n)
        ans++;
        ans+=min(mx,n-mx);
        mx*=2;
    
    printf("%d\\n",ans);


int main()
    int T;scanf("%d",&T);
    while(T--) solve();
    return 0;

C. Tree Infection


题意:
一棵树。每秒钟每个有儿子节点感染的节点i,可以感染节点i的一个其他健康儿子节点,同时可以指定一个健康节点感染。问最少多少时间,全部节点感染?
思路:
很容易想到,节点之间有关系只能是同属于一个节点的儿子,只有这样a感染了才能去感染b,不然b就只能指定感染,耗费时间。
那么我们可以将全部节点分成一些集合,划分条件为同一个节点的儿子节点。
然后秉承 “ 难 事 ” 先 做 , “ 易 事 ” 后 做 “难事”先做,“易事”后做 的原则,我们初始一次在指定感染的时候,按照集从大到小的顺序感染,这样每感染后一个节点时,我们可以之前的可以传递感染,这样在每个集合都有感染后,原来大集合已经感染的也最多,这样是最优的。
剩下的同样在这张规则下,继续指定感染。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,p[N],t1,t2;
vector<int>edge[N],num;

void solve()
    t1=t2=0; num.clear();
    scanf("%d",&n);
    edge[0].push_back(1);
    for(int i=2;i<=n;i++)
        int x; scanf("%d",&x);
        edge[x].push_back(i);
    

    for(int i=0;i<=n;i++)
        int len=edge[i].size();
        if(len!=0) num.push_back(len);
    
    sort(num.begin(),num.end());
    t1=num.size();
    priority_queue<int>q;
    for(int i=0;i<t1;i++) q.push(num[i]-(i+1));
    while(1)
        if(q.empty()) break;
        auto u=q.top(); q.pop();
        if(u<=0) continue;
        if(u<=t2) break;
        q.push(u-1); //指定感染
        t2++;
    
    printf("%d\\n",t1+t2);
    for(int i=0;i<=n;i++) edge[i].clear();


int main()
    int T;scanf("%d",&T);
    while(T--) solve();
    return 0;

D. GCD Guess


题意:
每个询问a,b ,得到输出gcd(a+x,b+x) ,最多询问30次,问x的值是多少,x的范围(1<=x<=1e9)
思路:
这题挺好想的吧,1e9正好只有30位,所以我们一位位去判断是否可行就好了。
我们判断第i为的0/1情况时,我们是得到 a + x = ( 1 < < i ) a+x=(1<<i) a+x=(1<<i),同时 b + x b+x b+x的第i位也是1,但是要比(1<<i)大。
这样如果 g c d ! = ( 1 < < i ) gcd!=(1<<i) gcd!=(1<<i),那么第i位取1。因为如果第i位为1的话,再加(1<<i),会使得第i位为0,这样gcd就肯定不会为(1<<i)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n;

int query(int a,int b)
    cout<<"? "<<a<<" "<<b<<endl;
    int gd;
    cin>>gd;
    return gd;


void print(int x)
    cout<<"! "<<x<<endl;


void solve()
    int ans=0;
    for(int bit=0;bit<30;bit++) //判断bit位
        int ned1=(1<<bit); 
        int ned2=(1<<bit)+(1<<30);
        int gd=query(ned1-ans,ned2-ans); //将[0,max(0,bit-1)]位的值减去
        if(gd!=ned1) ans+=ned1;
    
    print(ans);


int main()
    int T;scanf("%d",&T);
    while(T--) solve();
    return 0;

E. MinimizOR


题意:
有一个长度为n的序列,没有修改操作,q次查询。
每次去查询区间[l,r],输出其中不同位置两个元素的最小或值 i ! = j & & m i n ( a [ i ] ∣ a [ j ] ) i!=j\\&\\&min(a[i]|a[j]) i!=j&&min(a[i]a[j])
思路:
(神仙题)
我们知道或运算得到数值是看在二进制下某一位是否有1出现,有才会对结果做出贡献。
那么现在我们知道数据范围 ( 0 ≤ a [ i ] < 2 30 ) (0≤a[i]<2^30) (0a[i]<230),从高位往低位看,我们尽量保证选择的两个元素的高位没有1,等价于1尽可能的出现在低位。直接暴力判断肯定不好使,不过我们要让1尽可能出现在低位,也就意味着我们选择的数尽可能的小。
那么每次查询的时候,我们就找区间中最小的 m i n ( r − l + 1 , 31 ) min(r-l+1,31) min(rl+1,31)个数,然后两两组队,暴力判断最小或值。
这里可以用线段树维护,每次维护的区间记录区间最小值和最小值位置。每找到一个就记录一个,同时将其位置修改乘最大值,这样在这次之后的查询时不会重复利用,记得最后要修改回来。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=1e5+5;
const int inf=INT_MAX;
int n,q,a[N];
PII b[35];
struct node
    int l,r;
    PII mi_pos; //mi_pos.first=min, mi_pos.second=pos 
tr[N<<2];

void pushup(int u)tr[u].mi_pos=min(tr[u<<1].mi_pos,tr[u<<1|1].mi_pos);

void build(int u,int l,int r)
    tr[u]=l,r,inf,0;
    if(l==r)
        tr[u].mi_pos=a[l],l;
        return;
    
    int mid=(l+r)>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    pushup(u);


PII query(int u,int l,int r)
    if(l<=tr[u].l&&tr[u].r<=r) return tr[u].mi_pos;
    PII ans=inf,0;
    int mid=(tr[u].l+tr[u].r)>>1;
    if(l<=mid) ans=min(ans,query(u<<1,l,r));
    if(r>mid) ans=min(ans,query(u<<1|1,l,r));
    return ans;


void modify(int u,int x,int mi,int pos)
    if(tr[u].l==x&&tr[u].r==x)
        tr[u].mi_pos=mi,pos;
        return;
    
    int mid=(tr[u].l+tr[u].r)>>1;
    if(x<=mid) modify(u<<1,x,mi,pos);
    if(x>mid) modify(u<<1|1,x,mi,pos);
 

以上是关于Codeforces Round #781 (Div. 2)(ABCDE)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #781 (Div. 2)(ABCDE)

Codeforces Round #436 E. Fire(背包dp+输出路径)

[ACM]Codeforces Round #534 (Div. 2)

Codeforces 781C Underground Lab 构造

codeforces781D Axel and Marston in Bitland

Codeforces 781D Axel and Marston in Bitland 矩阵 bitset