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
否则走 2Fbi-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;
}

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

被折叠的 条评论
为什么被折叠?



