Codeforces 1110 简要题解

传送门
众所周知 l d x o i ldxoi ldxoi这种菜鸡选手是不会写 H H H题的,因此该篇博客只有 A A A题至 G G G题的题解,实在抱歉。

A题

传送门
题意简述:给出 b , k b,k b,k a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak b k − 1 a 1 + b k − 2 a 2 + . . . + a k b^{k-1}a_1+b^{k-2}a_2+...+a_k bk1a1+bk2a2+...+ak的奇偶性。


思路:分 b b b的奇偶性讨论下就好。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
const int N=1e5+5;
int b,k,a[N];
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int main(){
	b=read(),k=read();
	int ans=0;
	bool f=b&1;
	if(!f){
		int x;
		while(k--)x=read();
		cout<<(x&1?"odd":"even");
	}
	else{
		for(ri i=k-1;~i;--i)ans+=read()&1;
		cout<<(ans&1?"odd":"even");
	}
	
	return 0;
}

B题

传送门
题意简述:给出一条长为 m m m的木板,上面有 n n n个洞,你可以用不超过 k k k段木板去补洞问覆盖的最小长度。


思路:
如果 k ≥ n k\ge n kn直接输出 n n n
否则先把所有的用一根木板补。
然后可以抠掉中间 k − 1 k-1 k1段,于是我们把所有连续的两个洞的距离放进堆里取前 k − 1 k-1 k1大减掉即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
const int N=1e5+5;
int n,m,k,a[N];
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int main(){
	n=read(),m=read(),k=read();
	priority_queue<int>q;
	for(ri i=1;i<=n;++i)a[i]=read();
	sort(a+1,a+n+1);
	for(ri i=2;i<=n;++i)q.push(a[i]-a[i-1]-1);
	int delta=a[n]-a[1]+1;
	for(ri i=1;i<k;++i)delta-=q.top(),q.pop();
	cout<<delta;
	return 0;
}

C题

传送门
题意简述: n n n次询问( n ≤ 2000 n\le2000 n2000),每次给一个 a ( 2 ≤ a ≤ 2 25 − 1 ) a(2\le a\le2^{25}-1) a(2a2251),问对于所有满足 0 &lt; b &lt; a 0&lt;b&lt;a 0<b<a b b b g c d ( a &amp; b , a gcd(a\&amp;b,a gcd(a&b,a^ b ) b) b)的最大值是多少。


思路:

  1. 打表
    打表代码:
#include<bits/stdc++.h>
using namespace std;
int q,a,ans1[55]={0,2,3,4,7,8,15,16,31,32,63,64,127,128,255,256,511,512,1023,1024,2047,2048,4095,4096,8191,8192,16383,16384,32767,32768,65535,65536,131071,131072,262143,262144,524287,524288,1048575,1048576,2097151,2097152,4194303,4194304,8388607,8388608,16777215,16777216,33554431,33554432,67108863};
int ans2[55]={0,3,1,7,1,15,5,31,1,63,21,127,1,255,85,511,73,1023,341,2047,89,4095,1365,8191,1,16383,5461,32767,4681,65535,21845,131071,1,262143,87381,524287,1,1048575,349525,2097151,299593,4194303,1398101,8388607,178481,16777215,5592405,33554431,1082401,67108863,22369621};
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int main(){
	q=read();
	while(q--) {
		a=read();
		int loc=upper_bound(ans1+1,ans1+51,a)-ans1;
		cout<<ans2[loc-1]<<endl;
	}
	return 0;
}
  1. 考虑到分 a a a的情况讨论
    1. a ̸ = 2 k − 1 a\not=2^k-1 a̸=2k1那么取 b = ( 2 k − 1 ) a b=(2^k-1)^a b=(2k1)a即可,答案为 2 k − 1 2^k-1 2k1
    2. a = 2 k − 1 a=2^k-1 a=2k1,那么直接枚举其最大质因子,如果没有就输出 1 1 1

正解代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
const int N=1e5+5;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
inline int lowbit(const int&x){return x&-x;}
inline void solve1(const int&x){
	int up=1;
	while(up<=x)up<<=1;
	cout<<up-1<<'\n';
}
inline void solve2(const int&x){
	for(ri i=2;i*i<=x;++i)if(x%i==0){cout<<x/i<<'\n';return;}
	puts("1");
}
int main(){
	for(ri x,tt=read();tt;--tt){
		x=read();
		if(lowbit(x+1)!=x+1)solve1(x);
		else solve2(x);
	}
	return 0;
}

D题

传送门
题意简述:给 n n n张大小不超过 m m m的纸牌 ( n , m ≤ 1000000 ) (n,m\le1000000) (n,m1000000)
现在允许把满足任意一种条件的三张牌分为一组:

  1. 三张牌为连续自然数
  2. 三张牌相同

问分出来的最多的组数。


思路:
比赛的时候想了半天贪心发现是错的?手动滑稽233
然后就凉了。
考虑 d p dp dp
对于连续的三个数 x , x + 1 , x + 2 x,x+1,x+2 x,x+1,x+2,如果我们分成三次 ( x , x + 1 , x + 2 ) (x,x+1,x+2) (x,x+1,x+2)消耗的牌和分成 ( x , x , x ) , ( x + 1 , x + 1 , x + 1 ) , ( x + 2 , x + 2 , x + 2 ) (x,x,x),(x+1,x+1,x+1),(x+2,x+2,x+2) (x,x,x),(x+1,x+1,x+1),(x+2,x+2,x+2)是等价的。
于是对于连续的三张牌最多只用分两次 ( x , x + 1 , x + 2 ) (x,x+1,x+2) (x,x+1,x+2)
于是我们定义状态 f i , t 1 , t 2 f_{i,t1,t2} fi,t1,t2表示对于值为 1   i 1~i 1 i的牌,我们分 t 1 t1 t1 ( i − 1 , i , i + 1 ) (i-1,i,i+1) (i1,i,i+1),分 t 2 t2 t2 ( i , i + 1 , i + 2 ) (i,i+1,i+2) (i,i+1,i+2),然后枚举 f i + 1 , t 2 , t 3 f_{i+1,t2,t3} fi+1,t2,t3转移一下就行了,注意转移的时候如果值为 i + 1 i+1 i+1的牌有剩的需要全部分成 ( i + 1 , i + 1 , i + 1 ) (i+1,i+1,i+1) (i+1,i+1,i+1)
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=1e6+5;
int f[N][3][3],a[N],n,m;
int main(){
	n=read(),m=read();
	for(ri i=1;i<=n;++i)++a[read()];
	memset(f,-1,sizeof(f));
	f[0][0][0]=0;
	for(ri i=0;i<m+2;++i){
		for(ri t1=0;t1<=2;++t1){
			for(ri t2=0;t2<=2;++t2){
				if(~f[i][t1][t2]){
					for(ri t3=0;t3<=2;++t3){
						if(t1+t2+t3>a[i+1])continue;
						f[i+1][t2][t3]=max(f[i+1][t2][t3],f[i][t1][t2]+t3+(a[i+1]-t1-t2-t3)/3);
					}
				}
			}
		}
	}
	cout<<f[m+2][0][0];
	return 0;
}

E题

传送门
题意简述:给出两个数列 a n a_n an b n ( n ≤ 100000 ) b_n(n\le100000) bn(n100000),可以对 a i ( 2 ≤ i ≤ n − 1 ) a_i(2\le i\le n-1) ai(2in1)进行如下操作: a i = a i − 1 + a i + 1 − a i a_i=a_{i-1}+a_{i+1}-a_{i} ai=ai1+ai+1ai
问能否通过若干次操作使得 a , b a,b a,b完全一样。


思路:
定义 c i = a i − a i − 1 ( i ≤ 2 ) , c 1 = a 1 c_i=a_i-a_{i-1}(i\le2),c_1=a_1 ci=aiai1(i2),c1=a1
c c c a a a的差分数组。
发现每次操作就是交换 c i c_i ci c i + 1 c_{i+1} ci+1
于是把两个数列的差分数列排序看是否一样即可。
注意特判两个数的情况,这个时候并不能移动
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
const int N=1e6+5;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,m,a[N],b[N],c[N],d[N];
int main(){
	n=read();
	for(ri i=1;i<=n;++i)a[i]=read();
	for(ri i=1;i<=n;++i)c[i]=read();
	if(a[1]!=c[1]||a[n]!=c[n])puts("No"),exit(0);
	for(ri i=1;i<=n;++i)b[i]=a[i]-a[i-1],d[i]=c[i]-c[i-1];
	sort(b+1,b+n+1),sort(d+1,d+n+1);
	bool f=1;
	for(ri i=1;i<=n;++i)if(b[i]^d[i])f=0;
	if(f)puts("Yes");
	else puts("No");
	return 0;
}

F题

传送门
题意简述:给一棵 n n n个点的带边权树,有 q q q次询问 ( n , q ≤ 500000 ) (n,q\le500000) (n,q500000),每次问你在所有 d f s dfs dfs序在 [ l , r ] [l,r] [l,r]之间的叶子结点跟 x x x的最短距离。


思路:
考虑离线处理询问,把所有询问按照 x x x归类,于是只需动态维护在 d f s dfs dfs到某个节点的时候知道 d f s dfs dfs序在一段区间的叶子到当前点的距离最小值。
我们先按照题目中说的顺序遍历处理出 d f s dfs dfs序(其实就是用 v e c t o r vector vector存边)以及每个点到根的 d i s t dist dist,然后把叶子结点的 d i s t dist dist放入线段树里,这样就处理出了所有点到根节点的答案,然后考虑一个点向儿子节点移动的时候所有叶子结点到自己距离的变化,即儿子的子树里的叶子整体减掉当前边边权,其余的叶子加上当前边边权即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int N=5e5+5;
int n,m,fa[N],in[N],out[N],top=0,tot=0,q[N];
ll dis[N],ans[N];
vector<pii>e[N];
struct Node{int l,r,id;};
vector<Node>qry[N];
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (l+r>>1)
	ll mn[N<<2],add[N<<2];
	inline void pushup(int p){mn[p]=min(mn[lc],mn[rc]);}
	inline void pushnow(int p,ll v){mn[p]+=v,add[p]+=v;}
	inline void pushdown(int p){if(add[p])pushnow(lc,add[p]),pushnow(rc,add[p]),add[p]=0;}
	inline void build(int p,int l,int r){
		add[p]=0;
		if(l==r){mn[p]=dis[q[l]];return;}
		build(lc,l,mid),build(rc,mid+1,r),pushup(p);
	}
	inline ll query(int p,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr)return mn[p];
		pushdown(p);
		if(qr<=mid)return query(lc,l,mid,ql,qr);
		if(ql>mid)return query(rc,mid+1,r,ql,qr);
		return min(query(lc,l,mid,ql,mid),query(rc,mid+1,r,mid+1,qr));
	}
	inline void update(int p,int l,int r,int ql,int qr,ll v){
		if(ql>r||qr<l)return;
		if(ql<=l&&r<=qr)return pushnow(p,v);
		pushdown(p);
		if(qr<=mid)update(lc,l,mid,ql,qr,v);
		else if(ql>mid)update(rc,mid+1,r,ql,qr,v);
		else update(lc,l,mid,ql,mid,v),update(rc,mid+1,r,mid+1,qr,v);
		pushup(p);
	}
}
inline int findl(const int&x){return lower_bound(q+1,q+top+1,x)-q;}
inline int findr(const int&x){int ret=lower_bound(q+1,q+top+1,x)-q;return q[ret]>x?ret-1:ret;}
void dfs1(int p){
	in[p]=++tot;
	for(ri i=0,v;i<e[p].size();++i)dis[v=e[p][i].fi]=dis[p]+e[p][i].se,dfs1(v);
	out[p]=tot;
	if(!e[p].size())q[++top]=p;
}
void dfs2(int p){
	for(ri i=0,l,r;i<qry[p].size();++i)ans[qry[p][i].id]=SGT::query(1,1,top,qry[p][i].l,qry[p][i].r);
	for(ri i=0,l,r,v,w;i<e[p].size();++i){
		v=e[p][i].fi,l=findl(in[v]),r=findr(out[v]),w=e[p][i].se;
		SGT::update(1,1,top,l,r,-w),SGT::update(1,1,top,1,l-1,w),SGT::update(1,1,top,r+1,top,w);
		dfs2(v);
		SGT::update(1,1,top,l,r,w),SGT::update(1,1,top,1,l-1,-w),SGT::update(1,1,top,r+1,top,-w);
	}
}
int main(){
	n=read(),m=read();
	for(ri i=2;i<=n;++i)fa[i]=read(),e[fa[i]].push_back(pii(i,read()));
	dfs1(1),SGT::build(1,1,top);
	for(ri i=1;i<=top;++i)q[i]=in[q[i]];
	for(ri i=1,x,l,r;i<=m;++i){
		x=read(),l=findl(read()),r=findr(read());
		qry[x].push_back((Node){l,r,i});
	}
	dfs2(1);
	for(ri i=1;i<=m;++i)cout<<ans[i]<<'\n';
	return 0;
}

G题

传送门
题意简述:给出一棵树,上面有的点是白色的有的没有涂色,现在两个人轮流涂色,先手涂白色,后手涂黑色,当某一方涂出了一条长度为 3 3 3的路径时宣告胜利,如果全部涂完都没有满足条件即是和局,问双方都采取最优策略的最终结果。


思路:显然后手赢是不可能的 ,这辈子都不可能的
那么只需判断先手能不能赢了。
这个初始局的白色很烦人,因此我们改改条件,对于一个初始为白色的点 A A A,把它拓展成 4 4 4个点:
在这里插入图片描述

然后其余的边还是连向 A A A点,这个时候我们假如涂 A A A后手一定会涂 B B B,就回到了跟初始是白色等价的情况。

于是我们成功把问题转化成为了初始没有涂色的情况。
现在考虑先手胜利的条件:
第一种:
在这里插入图片描述
第二种:

在这里插入图片描述
第三种:
在这里插入图片描述
这种注意,方法是放最靠中间的两个:
在这里插入图片描述
这样子先手必胜。
注意这种情况中间的直链的长度必须要是奇数才行(大家可以手玩感受一下)
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=5e5+5;
int n=0,du[N*4],cnt[2];
vector<int>e[N*4];
char s[N];
void dfs(int p,int fa,int dist){
	if(e[p].size()>=3)++cnt[dist];
	for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])^fa)dfs(v,p,dist^1);
}
inline bool solve(const int&p){
	if(du[p]>=4)return 1;
	if(du[p]<=2)return 0;
	int cnt=0;
	for(ri i=0;i<e[p].size();++i)if(du[e[p][i]]>1)++cnt;
	return cnt>=2;
}
inline void add(int u,int v){
	e[u].push_back(v),++du[u];
	e[v].push_back(u),++du[v];
}
int main(){
	for(ri tt=read(),tot;tt;--tt){
		for(ri i=1;i<=n;++i)e[i].clear(),du[i]=0;
		n=read(),cnt[0]=cnt[1]=0;
		for(ri i=1,u,v;i<n;++i)u=read(),v=read(),add(u,v);
		scanf("%s",s+1),tot=n;
		for(ri i=1;i<=n;++i)if(s[i]=='W'){
			++tot;
			add(i,tot),add(tot,tot+1),add(tot,tot+2),tot+=2;
		}
		n=tot,dfs(1,0,0);
		if(cnt[0]>1||cnt[1]>1){puts("White");continue;}
		bool flag=0;
		for(ri i=1;i<=n;++i)flag|=solve(i);
		puts(flag?"White":"Draw");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值