ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-09-11 12:31:30  阅读:198  来源: 互联网

标签:int rep Codeforces fib 步长 Div 814 void dp


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有