其他分享
首页 > 其他分享> > Codeforces Round #814 (Div. 2) A-F

Codeforces Round #814 (Div. 2) A-F

作者:互联网

Codeforces Round #814 (Div. 2)

传送门

A

题意:棋子在左下角,两个人轮流操作,每次可以向上或向右移动奇数位,谁先无法操作谁输,给定棋盘大小,问最后谁赢。

分析:直接\((n+m)\&1\)即可。因为总共移动\(n+m-2\)次,以为移动奇数位,故在两个人移动后\(n+m-2\)的奇偶性不变,所以当总路程为偶数时,先手先无法操作,后手赢,否则先手赢。

void Solve(){
    int n,m;
    cin>>n>>m;
    if((n+m)&1) puts("Burenka");
    else puts("Tonya");
}  

B

题意:给定n,k,n为偶数,询问是否存在将1-n n个数分为n/2对,每对a,b满足 \((a+k)*b\%4=0\),若存在输出组对方案。

分析:
当k为奇数时:可以将a填奇数位,b填偶数位,可满足题意。
当k位偶数时:当k为4的倍数时,此时需满足a*b为4的倍数,不可满足;当k为2的倍数但不为4的倍数时,当a为2的倍数但不为4的倍数,b为奇数可成里,当a为4的倍数时,a,b互换即可。

void Solve(){
    int n,k;
    cin>>n>>k;
    if(k&1){
        puts("YES");
        rep(i,1,n){
            printf("%d %d\n",i,i+1);
            i++;
        }
    }
    else{
        if(k%4==0) puts("NO");
        else{
            puts("YES");
            for(int i=2;i<=n;i+=2){
                if(i%4==0) printf("%d %d\n",i-1,i);
                else printf("%d %d\n",i,i-1);
            }
        }
    }
}  

C

题意:n给运动员排成一队,每个运动员有一个能力值\(a_i\),每次排在前两的运动员进行比赛,赢得排在队首,输的到队尾。q次询问,每次询问在比赛k场比赛后,第i名运动员的胜利场次。

分析:实际在比赛一段时间后即可达到稳定状态:能力最强的人排在队首,而其余人轮流挑战他,然后只有最强的人一直在获胜,其余人不在获胜,模拟到稳态后直接回答询问即可。

struct node{int i,len,id;};
void Solve(){
    int n,q;cin>>n>>q;
    vector<int>a(n+1),num(n+1,0);
    deque<P>Q;
    rep(i,1,n) cin>>a[i],Q.pb(P(a[i],i));
    vector<node>que(q+1);
    vector<int>ans(q+1,-1);
    vector<vector<int>>G(n+1);
    rep(i,1,q){
        int u,v;
        scanf("%d %d",&u,&v);
        que[i]=(node){u,v,i};
        if(v<=n) G[v].pb(i);
    }
    int pos=0;
    while(1){
        P x=Q.front();Q.pop_front();
        P y=Q.front();Q.pop_front();
        if(x.first==n) break;
        pos++;
        if(x.first>y.first){
            num[x.second]++;
            Q.push_front(x);
            Q.push_back(y);
        }
        else{
            num[y.second]++;
            Q.push_front(y);
            Q.push_back(x);
        }
        for(auto z:G[pos]){
            ans[que[z].id]=num[que[z].i];
        }
    }
    rep(i,1,q){
        if(ans[i]==-1){
            if(a[que[i].i]==n) ans[i]=num[que[i].i]+que[i].len-pos;
            else ans[i]=num[que[i].i];
        }
    }
    rep(i,1,q){
        printf("%d\n",ans[i]);
    }
}  

D

题意:给定一个长度为n的数组,你需要将其变为全0,为此你可以进行下列操作:
选择一个区间\([l,r]\),对\(a_l\)到\(a_r\)的所有数异或x,代价为\(\lceil\frac{r-l+1}{2} \rceil\),与区间长度有关.
求最少的代价将数组变为全0.

分析:首先因为代价是\(\lceil\frac{r-l+1}{2} \rceil\),区间长度大于2的代价都可以分解为若干个区间小于等于2的.而对于区间长度为2的,除非两个数相等,否则依然要进行一次操作,因此大多数情况都是一个位置进行一次异或。最大代价是每个位置都进行一次异或。

推敲样例可以发现,当区间异或和为0时,这个区间消除的代价为区间长度减一,否则即为区间长度。至此,即可完成\(O(n^2)\)dp,通过D1,每次枚举那些区间异或和为0转移即可。

void solve(){
    int n=read();
    vector<int>a(n+1),dp(n+1,inf);
    vector<vector<int>>b(8192);
    rep(i,1,n) a[i]=read(),a[i]^=a[i-1];
    dp[0]=0;b[0].pb(0);
    rep(i,1,n){
        dp[i]=dp[i-1]+1;
        for(auto x:b[a[i]]) dp[i]=min(dp[i],dp[x]+(i-x-1));
        b[a[i]].pb(i);
    }
    print(dp[n]);pts;
}  

对于D2,因为只有区间异或和为0时可以减少代价,因此我们应将原数组京可能分为多个异或和为0的区间,所以在枚举异或和为0的区间时,只用枚举上一个异或前缀和相等的位置即可,否则代价只会变多。

void solve(){
    int n=read();
    vector<int>a(n+1),dp(n+1,inf);
    map<int,int>mp;
    rep(i,1,n) a[i]=read(),a[i]^=a[i-1];
    dp[0]=0;mp[0]=0;
    rep(i,1,n){
        dp[i]=dp[i-1]+1;
        if(mp.find(a[i])!=mp.end()) dp[i]=min(dp[i],dp[mp[a[i]]]+(i-mp[a[i]]-1));
        mp[a[i]]=i;
    }
    print(dp[n]);pts;
}  

E

题意:给定k种字符,每种字符有\(a_i\)个,问是否能将所有字符构成一个斐波那契串。

斐波那契串可表示为 \(fib_1*k_i+fib_2*k_j....fib_n*k_x\),其中\(k_i\)表示某一个字符,\(fib_i*k_j\)表示连续\(fib_i\)个\(k_j\),\(+\)即将字符串直接相连,并且相邻的\(k_i,k_{i+1}\)不能相等

分析:首先判断所有字符个数的和是否为斐波那契数列的某一个前缀和,如果不是直接NO。

之后因为斐波那契数列后面比前面大,因此我们直接倒序匹配,将每种字符个数用优先队列排列,每次取数量最多的与当前斐波那契项匹配,如果队首字符个数小于当前枚举的斐波那契项,直接NO。

因为一次可能有多个字符个数大于当前斐波那契项,这里进行分析讨论:

首先证明一个引理:
\(i为偶数fib_i=fib_{i-1}+fib_{i-3}+...+fib_1\)
\(i为奇数fib_i=fib_{i-1}+fib_{i-3}+...fib_0\)

假设当前匹配\(fib_i\),有两个字符的数量\(a_x\geq a_y> fib_i\),当字符\(a_x\)减去\(fib_i\)后,仍有\(a_y> fib_i=fib_{i-1}+fib_{i-3}+...\),即使剩下的所有项用\(a_y\)交叉排列,也会有多余,故此时无法满足题意,故当有两个及以上\(a_i>fib_i\)时输出NO.

同时当有两个以上相等的\(a_x=a_y=a_z=fib_i\)时,去掉\(a_x\)后,仍有\(a_y,a_z\)会大于剩下的所有项数和,不合法输出NO。

因此在优先队列中直接用个数最多的那个字符即可,因为只有1个大于当前\(fib_i\)或两个等于当前\(fib_i\)的情况合法,均等价于直接取最大的,但具体实现中我们直接取最大的即可,因为在后续步骤中一定会渐渐露出错误的马脚。

同时对于字符个数相等,应与上一项所填的不同,这里可在优先队列中用上一次使用位置来区分。具体看代码实现。

typedef pair<ll,ll> P;
ll fib[maxn];
void init(){
    fib[0]=fib[1]=1;
    rep(i,2,N){
        fib[i]=fib[i-1]+fib[i-2];
        if(fib[i]>1e12) {;break;}
    }
}
bool check(int &len,ll sum){
    ll s=0;
    rep(i,0,N){
        s+=fib[i];
        if(s==sum) {len=i;return 1;}
        else if(s>sum) break;
    }
    return 0;
}
void solve(){
    int k;k=read();
    priority_queue<P>q;
    ll sum=0;
    rep(i,1,k){
        int tmp=read();sum+=tmp;
        q.push({tmp,N});
    }
    int len=0;
    if(!check(len,sum)){ptn;return;}
    rpe(i,len,0){
        P now=q.top();q.pop();
        if(now.second!=i+1&&now.first>=fib[i]){
            q.push({now.first-fib[i],i});
        }
        else {ptn;return;}
    }
    if(q.top().first!=0) ptn;
    else pty;
}  

F

题意:给你一个长度为n的数组a,你有一个程序,输入为\((s,k),1\leq s\leq n,1\leq k \leq n-1\),表示从数组a的位置\(i=s\)开始,每次\(i=(i+k)>n? i+k-n:i+k\),统计所有\(a_i\)的和,定义为该次运行程序的价值。你需要找到所有可能输入中的最大价值。同时有q次更改,每次将\(a_p\)改为\(x\),再重新计算最大价值。

分析:
对于寻找最大价值,我们可以发现当k与n互质时,总是将所有的\(a_i\)均遍历一遍,而当k为n的每个因子\(x\)时,每次只会遍历\(\frac{n}{x}\)个数,因为因子个数有限,我们可以直接处理出每个因子\(x\)所对应的\(x\)个环对应的\(a_i\)的和,这个和再乘以\(x\)即是这个环的价值,因为每个数会遍历x次,每次遍历所有因子找所有环价值最大值即可。
修改时对每个因子只会修改一个环的价值,直接维护暴力修改即可。

综上方法没有问题,但在\(n\leq2e5\)内因子个数最多可为\(42\)个,维护最大值时可用set或线段树,复杂度\(logn\),\(q*42*logn=1.5e8\),再算上常数,挺玄,本地满数据吸氧直接5s+,至此再分析。

可以发现对于步长k对应的一个环,可分解步长2*k的两个环,同时这两个环其中一个的价值一定不略与步长k对应的一个环(此处相当于将一个数拆分为两个部分,再给这两个部分乘2,那其中一个部分一定大于等于原来的数),同样可分解成\(x*k\)个环,原理同上。至此步长为\(k\)的环可以被步长为\(x*k\)的环所表示。而因子\(y\)对应环的步长为\(y\),因子\(k*y\)对应的步长\(k*y\)故步长\(k*y\)对应的环可以表示所有步长为\(y\)的环。

此时最佳情况是步长为n,即只有一种情况,但题目规定\(k\not ={}n\),因此我们需要使等于若干个数包括n的除n以外所有因子即可。因此我们只需取\(k=\frac{n}{n的质因子}\)即可,这样即可包括除n以外的所有因子。

int prime[maxn],cnt=0,vis[maxn],from[maxn];
void init(){from[1]=1;
    rep(i,2,N){
        if(!vis[i]) prime[++cnt]=i,from[i]=i;;
        rep(j,1,cnt){
            if(i*prime[j]>N) break;
            vis[i*prime[j]]=1;
            from[i*prime[j]]=prime[j];
            if(i%prime[j]==0) break;
        }
    }
}
int yz[30],m;
void analy(int n){
    m=1;yz[1]=1;int tmp=n;
    while(1){
        if(n==1) break;
        yz[++m]=from[n];
        int tep=from[n];
        while(n%tep==0) n/=tep;
    }
    rep(i,1,m) yz[i]=tmp/yz[i];
    sort(yz+1,yz+1+m);
    m--;
}
multiset<ll>s[200];
void gett(){
    ll ans=-1;
    rep(i,1,m) 
        ans=max(ans,*(s[i].rbegin()));
    print(ans);pts;
}
void solve(){
    int n=read(),q=read();
    analy(n);
    vector<ll>a(n+1);
    vector<vector<ll>>b(m+1);
    rep(i,1,m) s[i].clear();
    rep(i,1,n) a[i]=read();   
    rep(i,1,m) {
        b[i].resize(yz[i]+1);
        rep(j,1,yz[i]){
            ll sum=0;
            for(int k=j;k<=n;k+=yz[i]) sum+=a[k];
            b[i][j]=sum;
            s[i].insert(sum*yz[i]);
        }
    }
    gett();
    while(q--){
        ll p=read(),x=read();
        rep(i,1,m){
            int id=p%yz[i];
            id= !id? yz[i]:id; 
            s[i].erase(s[i].find(b[i][id]*yz[i]));
            b[i][id]+=(x-a[p]);
            s[i].insert(b[i][id]*yz[i]);
        }
        a[p]=x;
        gett();
    }
}  

标签:int,rep,Codeforces,fib,步长,Div,814,void,dp
来源: https://www.cnblogs.com/Mr-leng/p/16683795.html