Codeforces Global Round 13 A-E题解

本文详细解答了Codeforces Global Round 13中A至E五道题目。题目涉及位操作、图形移动规律、递减贡献计算、位运算连边问题和斐波那契树的数学逻辑。通过分析和解题策略,帮助读者理解并解决这些算法问题。

A
01序列,一个操作对某一位取反,一个操作问你当前第k大是多少
维护一个当前最后一个1所在排名

#include <cstdio>
using namespace std;
const int MAX_N = 100025;
int arr[MAX_N];
int main()
{
    int n,m,sum = 0;
    scanf("%d%d",&n,&m);
    for(int i = 1;i<=n;++i)
    {
        scanf("%d",&arr[i]);
        sum+=arr[i];
    }
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(x==1)
        {
            if(arr[y]==0)
            {
                sum+=1;
                arr[y] = 1;
            }
            else
            {
                arr[y] = 0;
                sum-=1;
            }
        }
        else
        {
            if(y<=sum)
            {
                printf("1\n");
            }
            else
            {
                printf("0\n");
            }
        }
    }
    return 0;
}

B
比较有意思的一题 我们会发现只要纵坐标相邻之间绝对值<=1 就会必须要移动 否则不用移动
当一条竖线需要特判

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX_N = 100025;
int arr[MAX_N];
int main()
{
    int t;
    int n,u,v;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&u,&v);
        bool flag = true;
        int tmp = 0;
        for(int i = 1;i<=n;++i)
        {
            scanf("%d",&arr[i]);
            if(i==2)
            {
                tmp = arr[i] - arr[i-1];
                if(abs(arr[i]-arr[i-1])>1)
                {
                    flag = false;
                }
            }
            else if (i>=3)
            {
                if(abs(arr[i]-arr[i-1])>1)
                {
                    flag = false;
                }
            }
        }
        bool flag_ = false;
        sort(arr+1,arr+1+n);
        if(arr[1]==arr[n]) flag_ = true;
        if(flag_)
        {
            printf("%d\n",min(v*2,u+v));
        }
        else if (flag)
        {
            printf("%d\n",min(u,v));
        }
        else {
            printf("0\n");
        }
    }
    return 0;
}

C
我们发现某一位被递减至1时 他会对 i+2,i+3…i+Si 都做出一跳贡献
所以我们从右往左倒着做,维护每一位对后面的贡献
某一位如果已经到了1 此时过多的贡献会传递到下面

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX_N = 5025;
 
long long arr[MAX_N];
long long dp[MAX_N];
int main()
{
    long long ans = 0;
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        ans = 0;
        scanf("%d",&n);
        for(int i = 1;i<=n;++i)
        {
            scanf("%lld",&arr[i]);
            dp[i] = 0;
        }
        for(int i = n;i>=1;--i)
        {
            int l = i + 2;
            int r = min(1ll*n,i+arr[i]);
            for(int j = l;j<=r;++j)
            {
                dp[j]++;
                if(dp[j]>(arr[j]-1)) {int tmp = dp[j]-(arr[j]-1);dp[j+1]+=tmp,dp[j]-=tmp;}
            }
            while(r+2<=n&&r+1<=n&&dp[r+1]>(arr[r+1]-1)) {int tmp = dp[r+1]-(arr[r+1]-1);dp[r+2]+=tmp,dp[r+1]-=tmp,r++;}
        }
        for(int i = 1;i<=n;++i)
        {
            ans += max(0ll,arr[i]-1-dp[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

D
给你n m,定义连边,u,v u&v == v 则可以连一条单向边 u -> u+v 问你n能不能到m
我们不难发现m>=n才有解
那么还有如果m是 2^i - 1 那么肯定无解
并且还有一个性质m含1的个数要<=n 比赛的时候一直当做相等做 因为多个1只要顺序小 可以向前合成比原先少个数的1
相等时我们就知道顺序关系了 m每一个1都要在n同排名1左边 即从右到左统计下标 ith 1 ∈m 下标 >= ith 1 ∈n

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_N = 35;
 
int arr[MAX_N],brr[MAX_N],f[MAX_N];
 
 
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        for(int i = 1;i<=30;++i)
        {
            arr[i] = brr[i] = 0;
        }
        scanf("%d%d",&n,&m);
        if(m>=n)
        {
            int num = 0,num_ = 0,step = 0;
            int tmp = (1<<29);
            while(tmp)
            {
                if((n&tmp)!=0)
                {
                    arr[++num] = 30 - step+1;
                }
                if((m&tmp)!=0)
                {
                    brr[++num_] = 30 - step+1;
                }
                tmp/=2;
                step++;
            }
            bool flag = (num>=num_)?true:false;
            for(int i = 1;i<=min(num,num_);++i)
            {
                flag &= (arr[num-i+1]<=brr[num_-i+1])?true:false;
            }
            if(flag) printf("YES\n");
            else printf("NO\n");
        }
        else
        {
            printf("NO\n");
        }
    }
 
    return 0;
}

E. Fib-tree
数的大小为斐波那契数,并且斐波那契树可以分裂成(去掉一条边)两个合法的斐波那契树 此时斐波那契树合法 个数为1也被认为是合法的斐波那契树
数学归纳法证明 至多有两条边可以完成分裂
且两条边将 Fbi -> Fbi-1 + Fbi-2 = 2Fbi-2 + Fbi-3
所以我们先找重心,证明:因为第四个斐波那契树开始 i>=4, Fbi-1 > Fbi-2 又Fbi-1+Fbi-2 = Fbi
vis代表边是否被用过(分裂则为用过)
找到重心后dfs查找 Fbi-2 是否有两个 看之前证明
有一个则走 Fbi-1 + Fbi-2
否则走 2
Fbi-2 + Fbi-3
注意全局变量在递归中要用临时变量传递下个递归过程

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
 
const int MAX_N = 200025;
vector<pair<int,int> > vt[MAX_N];
bool vis[MAX_N];
int sz[MAX_N],f[35],xb,arr[MAX_N];
vector<pair<int,int> >VT;
 
void GG()
{
    printf("NO\n");
    exit(0);
}
 
void init()
{
    f[0] = f[1] = 1;
    for(int i =2;i<=30;++i)
    {
        f[i] = f[i-1] + f[i-2];
    }
}
 
int tree_rt,tree_minn;
void dfs0(int x,int fa,int n)
{
    int maxx = 0;
    sz[x] = 1;
    for(auto y:vt[x])
    {
        if(y.first!=fa&&!vis[y.second])
        {
            dfs0(y.first,x,n);
            sz[x]+=sz[y.first];
            maxx = max(maxx,sz[y.first]);
        }
    }
    if(maxx*2<n&&sz[x]*2>=n) tree_rt = x;
}
 
void dfs1(int x,int fa,int k)
{
    sz[x] = 1;
    for(auto y:vt[x])
    {
        if(y.first!=fa&&!vis[y.second])
        {
            dfs1(y.first,x,k);
            sz[x]+=sz[y.first];
            if(sz[y.first]==f[k-2])
            {
                VT.push_back(y);
            }
        }
    }
}
 
void solve(int n,int x)
{
    if(n<=3) return;
    int k = 0;
    while(f[k]<n) ++k;
    if(f[k]!=n) GG();
    tree_minn = 0x3f3f3f3f;
    dfs0(x,0,f[k]);int now_rt = tree_rt;
    vector<pair<int,int> >s_VT;
    swap(VT,s_VT);
    dfs1(now_rt,0,k);
    if(VT.size()==0) GG();
    if(VT.size()==1)
    {
        int V = VT[0].first;
        vis[VT[0].second] = true;
        solve(f[k-1],now_rt);
        solve(f[k-2],V);
    }
    else
    {
        int V = VT[0].first,V_ = VT[1].first;
        vis[VT[0].second] = vis[VT[1].second] = true;
        solve(f[k-2],V);
        solve(f[k-2],V_);
        solve(f[k-3],now_rt);
    }
}
 
 
int main()
{
    init();
    int n,x,y;
    scanf("%d",&n);
    memset(vis,false,sizeof(bool)*(n+1));
    for(int i = 1;i<n;++i)
    {
        scanf("%d%d",&x,&y);
        vt[x].push_back(make_pair(y,i));
        vt[y].push_back(make_pair(x,i));
    }
    solve(n,1);
    printf("YES\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值