CF Codeforces Global Round 13

传送门

为什么我这麽菜呀

A

思想不够,数据结构来凑,树状数组打了出来,很快的,过了。
但显然有只要把\(1\)的个数存下来,每次单点修改的时候计算总的\(1\)的个数就完了。
总结:我真傻,真的

B

首先题一定要申请呀呀呀呀呀呀呀呀呀
显然最多只用移动两步就可以了,那麽我们只需判断所有相邻两行障碍物的横向距离的最大值,
如果这个最大指大于\(1\),那麽就不用移动,直接输出\(0\)
如果这个最大值为\(1\),那麽只用移动一步,输出\(min(u,v)\)
如果这个最大值为\(0\),那麽只用移动\(2\)步,这时首先必须进行横向移动,然后进行横向或纵向,因此此时输出\(min(u+v,v+v)\)

n=read(),u=read(),v=read();
        int tmp=-1;
        rep(i,1,n) a[i]=read();
        rep(i,2,n) tmp=max(tmp,abs(a[i]-a[i-1]));
        if(tmp==0) {
            print(min(u+v,v+v));
        }
        else if(tmp>1) print(0);
        else print(min(u,v));
        pts; 

C

我太菜了
一个很显然的贪心,每次从第一个出发一定能取得最小的答案,不要相信样例的步骤,谁知道它是不是在误导你,那麽我们直接模拟肯定是不行的,对于每一个位置,它一定会转移到\((i+2,min(i+S_i,n))\)区间内所有的元素,那麽我们不妨令\(d[i]\)表示第\(i\)个位置消减的次数,
对于每个元素首先将它嫩便利到的位置\(d[i]++\),
随后如果当前位置\(d[i]\)大于\(S_i-1\),说明此时早已变为1,那麽踩到这里会自动到下一个位置,那麽\(d[i+1]+=d[i]-S_i+1\)
否则,\(ans+=S_i-1-d[i],d[i+1]\)不变

T=read();
    while(T--){
        ll ans=0;
        n=read();
        rep(i,1,n)s[i]=read(),d[i]=0;
        rep(i,1,n){
            rep(j,i+2,min(i+s[i],n)) d[j]++;  
            if(d[i]<s[i]-1) ans+=s[i]-1-d[i],d[i]=s[i]-1;
            d[i+1]+=abs(d[i]-s[i]+1);
        }
        print(ans);pts;
    }

D

这个\(u\)通向\(u+v\)的条件是\(u\&v=v\),
二进制数\(101000\)可以来自\(11000,100110,100111,100100\),我们可以发现将用二进制分解后,
\(u_i\)到\(v_i\)时,\(u_i\)上的1的个数一定大于等于\(v_i的个数\),因为\(\&\)的操作位\(00100\)这种时,每次只是将二进制每个位置上的1向左移动,1的数量不会发生变化。当操作\(001100\)时,1的数目可能变少,但不会变多。

其次因为\(v_i\)中的1可以拆解至较小位的若干个1,因此\(v_i\)只要依次的1的位置均不小于\(u_i\)的相应位置,那麽\(v_i\)上的1均可以通过一些变化来满足\(u_i\)的条件。

因此\(u_i\)到\(v_i\)的条件是:
1:\(u_i<=v_i\)
2:\(v_i\)中1的个数小于等于\(u_i\)
3: \(v_i\)与\(u_i\)的从小到大的二进制位为1的位置应满足\(v_i\)的大于\(u_i\)的,因为高位的1可以右移和变多。


bool solve(){
    if(x>y) return 0;
    a.clear();b.clear();
    rep(i,0,29) if(x&(1<<i)) a.pb(i);
    rep(i,0,29) if(y&(1<<i)) b.pb(i);
    if(b.size()>a.size()) return 0;
    int k=min(b.size(),a.size());
    rep(i,0,k-1){
        if(b[i]<a[i]) return 0;
    }
    return 1;
}
int main(){  
#ifndef ONLINE_JUDGE
    freopen("in0.txt","r",stdin);
#endif 
    n=read();
    
    while(n--){
        x=read(),y=read();
        if(solve()) puts("YES");
        else puts("NO");     
    }
    return 0;
}

E

这个题思路很显然,就是dfs,在dfs中如果当前子树的大小刚好为斐波那契数列的某一项,那麽就一定可以继续分,而dfs又可以满足从小子树到大子树,在小子树无法形成斐波那契树,那麽更大的树一定无法形成。

因此,我们以当前应分的两个子树的大小为关键字进行dfs,然后如果当前子树大小可以满足啊,那麽断掉这条边,继续向下验证,直至返回答案

int n;
int f[maxn];
int sze[maxn];
int last[maxn],cnt=1;
struct Edge{
    int v,nxt,cut;
}edge[maxn<<1];

void add(int u,int v){
    edge[++cnt].v=v;edge[cnt].nxt=last[u];
    last[u]=cnt;edge[cnt].cut=0;
}

bool dfs(int now,int fa,int x,int y){
    sze[now]=1;
    if(f[x]==1||f[y]==1) return 1;
    for(int i=last[now];i;i=edge[i].nxt){
        int to=edge[i].v;
        if(to==fa||edge[i].cut==1) continue;
        if(dfs(to,now,x,y)) return 1;
        if(sze[to]==f[y]){
            edge[i].cut=edge[i^1].cut=1;
            return dfs(to,to,y-2,y-1)&dfs(now,now,x-2,x-1);
        }
        if(sze[to]==f[x]){
            edge[i].cut=edge[i^1].cut=1;
            return dfs(to,to,x-2,x-1)&dfs(now,now,y-2,y-1);
        }
        sze[now]+=sze[to];
    }
    return 0;
}

int main(){  
#ifndef ONLINE_JUDGE
    freopen("in2.txt","r",stdin);
#endif 
    n=read();
    if(n<=3) {puts("YES");return 0;}
    f[0]=f[1]=1;int pos;
    rep(i,2,n){
        f[i]=f[i-1]+f[i-2];
        if(f[i]==n) {pos=i;break;}
        if(f[i]>n) {
            puts("NO");
            return 0;
        }
    }
    int x,y;
    rep(i,1,n-1){
        x=read(),y=read();
        add(x,y);add(y,x);
    }
    if(dfs(1,0,pos-2,pos-1)) puts("YES");
    else puts("NO");
    return 0;
}

F

题意

你有 \(n\)个磁铁,每个磁铁有属性,为下列三种之一

\(N、S、-\)(表示无磁性)
一次操作你可以选择不相交的两个磁铁的子集 \(S\) 和 \(T\),假设 S 中有 n1 个 N,s1 个 S, T 中有 n2 个 N,s2 个 S。这些你都不知道。通过交互库你可以得到 n1n2−n1s2−n2s1+s1s2(化简得到:(n1−s1)(n2−s2) )。得到的数值可能 <0。

你可以花费不超过 n+⌊log2n⌋ 次操作找出所有没有磁性的磁铁。

多组数据 T≤100,每次 n≤2000,∑n≤2000。保证至少两个有磁极的磁铁和一个无磁极的磁铁

做法

如果我们能够找到一个有磁性的位置,那麽我们一定能够通过这个位置来得到其他位置\(1\)的所有情况,那麽我们就需要想办法找出一个带磁性的的位置。

我们不妨每次查询位置i和i之前的所有元素,如果结果为\(0\),说明\(i\)是无磁性或者\(i-1\)之前均为无磁性的,但如果结果不为\(0\),那么说明\(i\)之前绝对存在有磁性的,且由于我们是从前向后遍历,因此\(i\)之前有且仅有一个有磁性的,此时\(i\)一定也为有磁性的。

那么我们就可以用位置\(i\)将后面的元素一个一个的判断出来,至此,复杂度为\(O(n)\),之后的\(logn\)用到二分上,我们每次询问\(1~mid\)和\(i\),然后找到i之前的哪一个有磁性的磁铁,至此,复杂度为\(O(n-1+logn)\)

int T;
int n;
vector<int>ans;
int ask(int pos,int l,int r){
    printf("? 1 %d\n",r-l+1);
    print(pos);pts;
    rep(i,l,r) print(i),ptc;
    pts;
    fflush(stdout);
    return read();
}
 
int main(){  
    T=read();
    while(T--){
        n=read();
        ans.clear();
        rep(i,2,n){
            int k=ask(i,1,i-1);
            if(k==0) continue;
            rep(j,i+1,n) {
                int tmp=ask(i,j,j);
                if(tmp==0) ans.pb(j);
            }
            int l=1,r=i-1,pos;
            while(l<=r){
                int mid=(l+r)>>1;
                if(ask(i,1,mid)) r=mid-1,pos=mid;
                else l=mid+1;
            }
            rep(j,1,i-1) {
                if(j!=pos) ans.pb(j);
            }
            break;
        }
        printf("! %d ",ans.size());
        int x=ans.size();
        rep(i,0,x-1) print(ans[i]),ptc;
        pts;fflush(stdout);
    }
    return 0;
}
上一篇:「USACO 2021.1 Platinum」Sum of Distances


下一篇:「WC2021」表达式求值