Codeforces Global Round 1题解报告(A,B,C,D,E,F)

本文详细解析了Codeforces Global Round 1的六道题目,涵盖模拟、贪心、数论、动态规划、思维挑战及线段树与离线操作等算法技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://codeforces.com/contest/1110/problem

第一题(模拟):

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll unsigned long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
const int mod=2;
const int maxn=1e5+100;
const int maxm=1e5+100;
const int ub=1e6;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}

ll b,k,x;
ll ans=0;
int main()
{
    cin>>b>>k;
    rep(i,0,k){
        cin>>x;
        ans=(ans+x*powmod(b,k-i-1)%2)%2;
    }
    if(ans&1) puts("odd");
    else puts("even");
    return 0;
}

第二题(贪心):

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll unsigned long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
const int mod=1e9+7;
const int maxn=1e5+100;
const int maxm=1e5+100;
const int ub=1e6;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}

struct node{
    int len,x,y;
    bool operator<(const node& q){
        return len<q.len;
    }
}p[maxn];
int a[maxn];
set<int> st;
int n,m,k;
int main()
{
    cin>>n>>m>>k;
    int ans=0;
    rep(i,0,n) cin>>a[i],st.insert(a[i]);
    rep(i,0,n-1) p[i].len=a[i+1]-a[i]+1,p[i].x=a[i],p[i].y=a[i+1];
    sort(p,p+n-1);
    ///cout<<p[0].len<<endl;
    rep(i,0,n-1){
        ///cout<<p[i].x<<p[i].y<<endl;
        if(st.size()==k) break;
        k--,ans+=p[i].len;
        if(st.find(p[i].x)!=st.end()) st.erase(p[i].x);
        else k++,ans--;
        if(st.find(p[i].y)!=st.end()) st.erase(p[i].y);
        else k++,ans--;
    }
    ans+=k;
    cout<<ans<<endl;
    return 0;
}

第三题(数论):

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll unsigned long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
const int mod=1e9+7;
const int maxn=1e5+100;
const int maxm=1e5+100;
const int ub=1e6;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}

int x,tx;
int a[30],cnt=0,ans;
int main()
{
    int q;cin>>q;
    while(q--)
    {
        cin>>x;tx=x;
        cnt=0;
        while(x) a[cnt++]=x&1,x>>=1;
        int flag=1;rep(i,0,cnt) flag&=a[i];
        if(flag==0) ans=(1<<cnt)-1;
        else{
            int i,tmp=0;
            for( i=2;i*i<=tx;i++) if(tx%i==0) {tmp=1;tx/=i;break;}
            if(!tmp) ans=1;
            else ans=tx;
        }
        cout<<ans<<endl;
    }



    return 0;
}

第四题(DP):

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
const int mod=1e9+7;
const int maxn=1e6+10;
const int ub=1e6;
const double inf=5e-4;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
/*
题目大意:给定若干块有类型的砖块,
要求类型序号连续的三元组和序号相同的三元组可以组成一个,
问给定的类型序列最多可以组成多少块。

题目分析:这道题的DP思维感觉还是蛮巧妙的,
也是我没有见过的一种划分状态的方法。
在比赛中我是想贪心看看的,无奈有一种情况即样例中的情况,
1,2,3,2,1,这种情况我贪心策略是失效的,特判也没有用。。

赛后看题解才知道是DP解法,首先我们不难发现如果有三个连续的序号序列和
三个序号相同的三元组,那么肯定是换算成三个序号相同的三元组来的划算。
所以我们可以知道任意连续的三元组,其个数是0,1,2。

我们定义DP状态:dp[i][j][k],在i位置,有j个形如i-1,i,i+1的三元组,
有k个形如i,i+1,i+2的三元组,关于权重的处理,dp[i][j][k]和dp[i+1][k][p]之间,
i+1位置上的数若想要组成类型相同的三元组,可以组成(cnt[i+1]-j-k-p)/3,再加上p个,
这样构成转移方程,其中p是属于新状态,j,k属于旧状态。

这样的方程还有些细节没有搞明白,因为我们并不知道i+1位置后面会不会真正的出现p个二元组来
满足这样的条件。我们首先可以用一个序列表示其做出的三元组选择(连续的类型),
那么根据DP的准则,其组合优化的过程是把任何一个位置的变化丢到后续的状态影响中,
我们不妨先类比,如果题目要求是二元组而不是三元组呢?
不难想到dp[i][j],j表示i,i+1这样的二元组的个数,其上一个位置转移过来做出来的贡献,
可以被消去再恢复,这样我们可以把选择序的最优序列描述出来了,
因为最优选择序列肯定是有个间端点其以某个数结尾,而DP方程恰可以表述以该数结尾的所有情况。
三元组也是一个道理。

最后答案应该是dp[m+1][0][0],因为m位置上的贡献还可能包括其相同三元组的贡献。
*/
int n,m,x,cnt[maxn];
int dp[maxn][3][3];
int main(){
    cin>>n>>m;
    rep(i,1,n+1) {cin>>x;cnt[x]++;}
    mst(dp,-1),dp[0][0][0]=0;///初始化
    rep(i,0,m+1) rep(j,0,3) rep(k,0,3){///枚举过去的状态
        int tmp=cnt[i+1]-j-k;
        if(dp[i][j][k]==-1) continue;
        for(int p=0;p<3&&p<=tmp;p++)
            dp[i+1][k][p]=max(dp[i+1][k][p],dp[i][j][k]+(tmp-p)/3+p);
    }
    cout<<dp[m+1][0][0]<<endl;
    return 0;
}

第五题(思维脑洞):

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
const int mod=1e9+7;
const int maxn=1e5+5;
const int ub=1e6;
const double inf=1e-4;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
/*
题目大意:
给定两个序列,
每次可以在第一个序列上执行一个操作
即:a[i]=a[i-1]+a[i+1]-a[i],问是否可能
把a序列经过若干次操作后变成b序列。

题目分析:
很有思维的一道题目。
考虑a[i-1],a[i],a[i+1],
当我们把a[i]变成a[i-1]+a[i+1]-a[i]后,
a[i+1]-a[i]变成为a[i]-a[i-1],
a[i]-a[i-1]变成为a[i+1]-a[i],
不难发现一次操作后两个差交换了,
所以操作的本质就是交换相邻差的过程。
那么就好判断了,把两个序列所有的相邻差都存起来比较下即可。
*/
ll n,a[maxn],b[maxn];
ll tmp1[maxn],cnt1=0,tmp2[maxn],cnt2=0;
int main(){
    cin>>n;
    rep(i,1,n+1) cin>>a[i];
    rep(i,1,n+1) cin>>b[i];
    rep(i,1,n) tmp1[cnt1++]=a[i+1]-a[i];
    rep(i,1,n) tmp2[cnt2++]=b[i+1]-b[i];
    sort(tmp1,tmp1+cnt1);
    sort(tmp2,tmp2+cnt2);
    int flag=0;
    rep(i,0,cnt1) if(tmp1[i]!=tmp2[i]) {flag=1;break;}
    if(a[1]!=b[1]||a[n]!=b[n]) flag=1;
    if(flag) puts("No");
    else puts("Yes");
    return 0;
}

第六题(线段树+离线操作):

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
const int mod=1e9+7;
const int maxn=5e5+5;
const int ub=1e6;
const double inf=1e-4;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
/*
题目大意:
统计查询类题目,给定一颗树,树边有权重,
查询格式是:x,y,z,查询x节点到[y,z]区间中的叶节点的
最短路径长度是多少。其树的产生形式严格遵循DFS序形式。

题目分析:
这道题我是瞄了眼题解的思路的,才知道有个换根的性质。
大体是这样的:
首先考虑所有的x都是1根节点,那么我们只要把叶节点插入到线段树里面查询即可。
然后考虑状态转移,即dp[u]与dp[v]的关系,
把以u为根节点的线段树的状态,转移到以v为根节点的线段树的状态,
不难发现,v子树中所有点其距离都减少了e(u,v),其余的都增加了e(u,v),
那么这个就用线段树维护区间性质,其子树的区间可以用DFS序事先维护出来,
然后我们用离线操作按x来存储查询,最后深搜的时候统计答案即可。
详见代码,大部分都是模板操作,最终的思路是在solve函数里面。
时间复杂度为O(mlogn+n).
*/
int n,q,x,y,z;
int pl[maxn],pr[maxn],d[maxn],tot=0;///DFS序
vector<pair<int,int>> g[maxn];///存储边关系
struct Q{int y,v,id;};
vector<Q> qy[maxn];
ll tree[maxn<<2],lazy[maxn<<2];
ll dis[maxn],ans[maxn],INF=1e18;
void build(lrt){
    lazy[rt]=0,tree[rt]=INF;
    if(l==r){
        if(d[l]==1) tree[rt]=dis[l];
        return;
    }
    int mid=l+r>>1;
    build(lson),build(rson);
    tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
void pushdown(lrt){
    if(lazy[rt]){
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        tree[rt<<1]+=lazy[rt];
        tree[rt<<1]=min(tree[rt<<1],INF);
        tree[rt<<1|1]+=lazy[rt];
        tree[rt<<1|1]=min(tree[rt<<1|1],INF);
        lazy[rt]=0;
    }
}
void update(lrt,int L,int R,int v){
    if(L>R) return ;
    if(L<=l&&r<=R){
        if(tree[rt]!=INF){
            tree[rt]-=v;
            lazy[rt]-=v;
        }
        return ;
    }
    pushdown(root);
    int mid=l+r>>1;
    if(L<=mid) update(lson,L,R,v);
    if(mid<R) update(rson,L,R,v);
    tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
ll query(lrt,int L,int R){
    if(L<=l&&r<=R){
        return tree[rt];
    }
    pushdown(root);
    int mid=l+r>>1;
    ll ret=INF;
    if(L<=mid) ret=min(ret,query(lson,L,R));
    if(mid<R) ret=min(ret,query(rson,L,R));
    return ret;
}
void dfs(int u,int fa){
    pl[u]=++tot;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i].fi,len=g[u][i].se;
        if(v==fa) continue;
        dis[v]=dis[u]+len;
        dfs(v,u);
    }
    pr[u]=tot;
}
void solve(int u,int fa){
    rep(i,0,qy[u].size()){
        int y=qy[u][i].y,v=qy[u][i].v,id=qy[u][i].id;
        ans[id]=query(1,n,1,y,v);
    }
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i].fi,len=g[u][i].se;
        if(v==fa) continue;
        update(1,n,1,pl[v],pr[v],len);
        update(1,n,1,1,pl[v]-1,-len),update(1,n,1,pr[v]+1,n,-len);
        solve(v,u);
        update(1,n,1,pl[v],pr[v],-len);
        update(1,n,1,1,pl[v]-1,len),update(1,n,1,pr[v]+1,n,len);
    }
}
int main(){
    scanf("%d%d",&n,&q);
    rep(i,2,n+1){
        scanf("%d%d",&x,&y);
        g[i].push_back(make_pair(x,y));
        g[x].push_back(make_pair(i,y));
        d[i]++,d[x]++;
    }
    rep(i,0,q){
        scanf("%d%d%d",&x,&y,&z);
        qy[x].push_back(Q{y,z,i});
    }
    dis[1]=0,tot=0;
    dfs(1,-1),build(1,n,1);
    solve(1,-1);
    rep(i,0,q) printf("%lld\n",ans[i]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值