Codeforces Global Round1 题解

A

如果 b b b为偶数,那么只需关心 a a a的最后一位的奇偶性即可。

如果 b b b为奇数,那么显然 a a a ∑ i = 1 k a i \sum_{i=1}^ka_i i=1kai奇偶性相同。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
    int a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}
ll rdll(){
    ll a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}

const int N=100005;

int b,n,a[N],s;

int main(){
	scanf("%d%d",&b,&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	if(b&1)for(int i=1;i<=n;i++)s=(s^a[i])&1;
	else s=a[n]&1;
	if(s)printf("odd");else printf("even");
	return 0;
}

B

如果只能使用 k k k次胶带,显然在 n n n个点的 n − 1 n-1 n1个间隔中取 n − k n-k nk个连接。

贪心,直接取间隔长度最小的即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
    int a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}
ll rdll(){
    ll a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}

const int N=100005;

int n,m,k,a[N],b[N];
ll s;
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)scanf("%d",&b[i]);k=n-k;
	for(int i=1;i< n;i++)a[i]=b[i+1]-b[i]-1;sort(a+1,a+n);
	for(int i=1;i<=k;i++)s+=a[i];
	printf("%I64d",s+n);
	return 0;
}

C

看到公式,乱糟糟的,毫无头绪?先打表找规律试试。

发现一个规律:

对于 a a a,设 x = max ⁡ x  s.t.  2 x − 1 ≤ a x=\max x\text{ s.t. }2^x-1\le a x=maxx s.t. 2x1a,则如果 a ≠ 2 x − 1 a\ne2^x-1 a̸=2x1那么 f ( a ) = 2 x − 1 f(a)=2^x-1 f(a)=2x1

如果 a = 2 x − 1 a=2^x-1 a=2x1怎么办?看起来没规律?直接打表。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
    int a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}
ll rdll(){
    ll a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}

const int N=100005;

int n,t;

int main(){
	scanf("%d",&t);
	while(t--){
	    scanf("%d",&n);
        if(n==1)printf("0\n");
        else if(n==3)printf("1\n");
        else if(n==7)printf("1\n");
        else if(n==15)printf("5\n");
        else if(n==31)printf("1\n");
        else if(n==63)printf("21\n");
        else if(n==127)printf("1\n");
        else if(n==255)printf("85\n");
        else if(n==511)printf("73\n");
        else if(n==1023)printf("341\n");
        else if(n==2047)printf("89\n");
        else if(n==4095)printf("1365\n");
        else if(n==8191)printf("1\n");
        else if(n==16383)printf("5461\n");
        else if(n==32767)printf("4681\n");
        else if(n==65535)printf("21845\n");
        else if(n==131071)printf("1\n");
        else if(n==262143)printf("87381\n");
        else if(n==524287)printf("1\n");
        else if(n==1048575)printf("349525\n");
        else if(n==2097151)printf("299593\n");
        else if(n==4194303)printf("1398101\n");
        else if(n==8388607)printf("178481\n");
        else if(n==16777215)printf("5592405\n");
        else if(n==33554431)printf("1082401\n");
	    else{
	        int a=1;
	        while(a<n)a=a<<1|1;
	        printf("%d\n",a);
	    }
	}
	return 0;
}

D

第一眼感觉贪心不可做?然后发现一个显然的性质:

存在一个最优解,使对于任意 i i i [ i , i + 1 , i + 2 ] [i,i+1,i+2] [i,i+1,i+2]最多出现 2 2 2次。

接下来就可以无脑DP了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
    int a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}
ll rdll(){
    ll a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}
void chkmax(ll &a,ll b){if(a<b)a=b;}

const int N=1000005;

int n,m,a[N];
ll d[N][3][3],v[N][3][3],s;

int main(){
	m=rd(),n=rd();while(m--)++a[rd()];
	v[0][0][0]=1;
	for(int i=1;i<=n;i++)for(int j=0;j<3;j++)for(int k=0;k<3;k++)for(int l=0;l<3;l++){
	    if(!v[i-1][j][k])continue;
	    if(i>n-2&&l)continue;
		if(j+k+l<=a[i]){
			chkmax(d[i][k][l],d[i-1][j][k]+(a[i]-j-k-l)/3+l);
			v[i][k][l]=1;
		}
	}
	printf("%I64d",d[n][0][0]);
	return 0;
}

E

一个重要结论:对于一次在序列 A A A上的操作, A A A的差分序列 A ′ A&#x27; A恰好交换了相邻的2项。

所以检查首尾和排序后的差分序列即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
    int a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}
ll rdll(){
    ll a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}

const int N=100005;

int n,a[N],b[N];

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)scanf("%d",&b[i]);
	if(a[1]!=b[1]||a[n]!=b[n]){printf("No");return 0;}
	for(int i=1;i<n;i++)a[i]=a[i+1]-a[i],b[i]=b[i+1]-b[i];
	sort(a+1,a+n),sort(b+1,b+n);
	for(int i=1;i<n;i++)if(a[i]!=b[i]){printf("No");return 0;}
	printf("Yes");
	return 0;
}

F

不难发现,每次把当前节点 u u u移动到它的某个儿子 v v v时,

v v v子树内的叶子节点到当前节点距离减少 w ( u , v ) w(u,v) w(u,v),其他叶子节点到当前节点距离增加 w ( u , v ) w(u,v) w(u,v)

由于图的节点按DFS序编号,所以一个点子树内叶子节点的编号都在某个区间内。

所以更换当前节点可以转换为有限次区间加。

至于询问?离线,当DFS到某个点时回答从这个点出发的询问,直接转换为区间最小值即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
    int a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}
ll rdll(){
    ll a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}

const int N=500005;
const ll inf=1000000000000000ll;
int n,q,cnt,st[N],ed[N];
vector<int>e[N],f[N],ql[N],qr[N],qi[N];
ll d[N],tre[N<<2],lazy[N<<2],ans[N],D;
void dfs(int v,int fa,ll dep){
    if(e[v].size())d[v]=inf;else d[v]=dep;
    st[v]=++cnt;
    for(int i=0;i<(int)e[v].size();i++)dfs(e[v][i],v,dep+f[v][i]);
    ed[v]=  cnt;
}
void in();
#define mid ((l+r)>>1)
#define ls o<<1,l,mid
#define rs o<<1|1,mid+1,r
void pu(int o){tre[o]=min(tre[o<<1],tre[o<<1|1]);}
void pt(int o,ll v){tre[o]+=v,lazy[o]+=v;}
void pd(int o){pt(o<<1,lazy[o]),pt(o<<1|1,lazy[o]),lazy[o]=0;}
void build(int o,int l,int r){
    if(l==r){tre[o]=d[l];return;}
    build(ls),build(rs),pu(o);
}
void add(int o,int l,int r,int L,int R,ll v){
    if(r<L||R<l)return;
    if(L<=l&&r<=R){pt(o,v);return;}
    pd(o),add(ls,L,R,v),add(rs,L,R,v),pu(o);
}
ll query(int o,int l,int r,int L,int R){
    if(r<L||R<l)return inf;
    if(L<=l&&r<=R)return tre[o];
    pd(o);return min(query(ls,L,R),query(rs,L,R));
}
void solve(int v,int fa,int la){
    D+=la,add(1,1,n,st[v],ed[v],-la-la);
    for(int i=0;i<(int)qi[v].size();i++)ans[qi[v][i]]=D+query(1,1,n,ql[v][i],qr[v][i]);
    for(int i=0;i<(int)e[v].size();i++)solve(e[v][i],v,f[v][i]);
    D-=la,add(1,1,n,st[v],ed[v], la+la);
}
int main(){
	in();
	dfs(1,0,0);
	build(1,1,n);
	solve(1,0,0);
	for(int i=1;i<=q;i++)printf("%I64d\n",ans[i]);
	return 0;
}
void in(){
	n=rd(),q=rd();
	for(int i=2;i<=n;i++){
	    int x=rd(),y=rd();
	    e[x].push_back(i);
	    f[x].push_back(y);
	}
	for(int i=1;i<=q;i++){
	    int x=rd(),y=rd(),z=rd();
	    ql[x].push_back(y);
	    qr[x].push_back(z);
	    qi[x].push_back(i);
	}
}

G

首先,可以把白点转换为无色点:

WtoN

显然在ABCD中双方都无法获胜。如果白子游戏开始先抢占A,那么黑子必须抢占B,而C、D就不重要了。


现在考虑只有无色点的情况。

如果发生以下2种情况,那么白子获胜:

有度数 ≥ 4 \ge4 4的点有度数 ≥ 3 \ge3 3且有 ≥ 2 \ge2 2个非叶子邻居的点
D4D3

显然,如果有 ≥ 3 \ge3 3个度数 ≥ 3 \ge3 3的点,那么白子同样获胜。

那么其他情况,看似一定打平。但有一个例外:

OddBone

如果“骨头”的中间部分长度为奇数(这张图中长度为5),那么白子同样可以获胜。

其他情况可以证明是平局。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int rd(){
    int a=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
    return a;
}
const int N=2000005;
int t,n,m,d[N];
char c[N];
vector<int>e[N];
void add(int v,int u){e[v].push_back(u);e[u].push_back(v);}
void app(int v){e[++n].clear();add(v,n);}
int ac(){
	int v=0;
	for(int i=1;i<=n;i++)if(d[i]>3)return 4;
	for(int i=1;i<=n;i++)if(d[i]>2){
		++v;int x=0;
		for(int j=0;j<d[i];j++)if(d[e[i][j]]!=1)++x;
		if(x>1)return 3;
	}
	if(v>2)return 2;
	return v==2&&(n&1);
}
int main(){
	t=rd();
	while(t--){
	    m=n=rd();
	    for(int i=1;i<=n;i++)e[i].clear();
	    for(int i=1;i<n;i++)add(rd(),rd());
	    scanf("%s",c+1);
	    for(int i=1;i<=m;i++)if(c[i]=='W'){app(i);int v=n;app(v);app(v);}
		for(int i=1;i<=n;i++)d[i]=(int)e[i].size();
		if(ac())puts("White");else puts("Draw");
	}
	return 0;
}

H

咕咕咕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值