AtCoder Grand Contest 010题解
\(A\)
判一下奇数的个数就行了
const int N=1e5+5;
int a[N],n,res;
int main(){
scanf("%d",&n);
fp(i,1,n)scanf("%d",&a[i]),res^=(a[i]&1);
puts(res?"NO":"YES");
return 0;
}
\(B\)
首先记录一下所有数的总和\(sum\),然后判断一下加到这个\(sum\)需要进行几次操作
我们考虑能否把该序列变为\(0\),把序列给差分,即令\(b_i=a_i-a_{i-1},b_1=a_1-a_n\),那么发现一次操作之后会令所有\(b_i-=1\)以及某一个\(b_i+=n\)
我们最终的目的首先要让所有\(b_i\)变为\(0\),又因为所有\(b_i\)的和为\(0\)且一次操作之后总和不变,我们只要让所有\(b_i\)相等即可,而操作的时候只有\(+n\)这个操作会改变相对大小,那么判断一下所有数能否通过\(+n\)变为最大的数就好了
如果可以,那么记此时需要的操作次数为\(cnt\),如果满足\(sum\geq cnt\)并且\(sum-cnt\)是\(n\)的倍数就说明可行了
//quming
#include<bits/stdc++.h>
#define R register
#define int long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e5+5;
int a[N],b[N],n,sum,res;
signed main(){
scanf("%lld",&n);
fp(i,1,n)scanf("%lld",&a[i]),sum+=a[i];
if(sum%(n*(n+1)>>1))return puts("NO"),0;
sum/=(n*(n+1)>>1);
fp(i,2,n)b[i]=a[i]-a[i-1];b[1]=a[1]-a[n];
sort(b+1,b+1+n);
fp(i,1,n-1)if((b[i+1]-b[i])%n)return puts("NO"),0;
fp(i,1,n-1)res+=(b[n]-b[i])/n;
if(sum<res||(sum-res)%n)return puts("NO"),0;
puts("YES");
return 0;
}
\(C\)
随便选一个不是叶子的点做根,那么对于一个不是叶子的点,经过它的路径必然有一端来自它的一棵子树。对于点\(u\),假设它的子树里会分别延伸出\(f_v\)条路径,记\(sum=\sum f_v\),\(cnt\)为相互连接了的路径条数,那么\(u\)被覆盖的次数就是\(sum-cnt\),而且剩下的\(sum-cnt*2\)条路径会继续往上延伸
那么我们记\(f_u\)表示使\(u\)以及子树内都满足条件,此时\(u\)需要往上延伸多少条路径,树形\(dp\)即可,记得最后判一下\(f_1\)是否为\(0\)
具体细节看代码
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=1e5+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
ll f[N];int deg[N],a[N],rt,n;
void dfs(int u,int fa){
if(deg[u]==1)return f[u]=a[u],void();
R ll sum=0,mx=0;
go(u)if(v!=fa)dfs(v,u),cmax(mx,f[v]),sum+=f[v];
R ll cnt=((mx<<1)<=sum?sum>>1:sum-mx);
if(sum<a[u]||sum-a[u]>cnt){puts("NO");exit(0);}
f[u]=sum-((sum-a[u])<<1);
}
int main(){
scanf("%d",&n);
fp(i,1,n)scanf("%d",&a[i]);
for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u),++deg[u],++deg[v];
if(n==2)return puts(a[1]==a[2]?"YES":"NO"),0;
fp(i,1,n)if(deg[i]!=1){rt=i;break;}
dfs(rt,0);
puts(f[rt]?"NO":"YES");
return 0;
}
\(D\)
博弈日常不会做
这题的结论:如果有奇数个偶数则先手必胜
首先这里的所有数\(\gcd\)为\(1\),代表着所有数里至少有一个奇数(很重要的性质,然而并没有发现)
那么先手一开始先将一个偶数变为奇数,然后就可以搬把小凳子来等着赢了:
后手如果选择将一个奇数变为偶数,先手可以选择一个偶数使其变为奇数
先手如果选择将一个偶数变为奇数,先手可以选择另一个偶数使其变为奇数
那么无论何时偶数的个数都为偶数,且因为这个过程中序列中至少存在一个奇数,所以\(\gcd\)绝对不会是偶数,也就是说奇偶的个数不会改变
也就是说先手赢定了
那么后手只能等死了么?或者如果有偶数个偶数就先手必胜?
回来考虑有偶数个偶数的情况,如果奇数的个数大于\(1\),那么先手不管怎么操作都会导致都会变成上面那种局面导致后手必赢,不过如果此时奇数的个数为\(1\),那么先手令这个奇数变为偶数,会导致“所有数中必定有一个奇数”这个条件不存在了,那么我们就递归考虑就可以了
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e5+5;
int a[N],n;
bool solve(){
fp(i,1,n)if(a[i]&1){
if(a[i]==1)return 0;
--a[i];break;
}
R int d=0;
fp(i,1,n)d=__gcd(d,a[i]);
fp(i,1,n)a[i]/=d;
R int t=0,sz=0;
fp(i,1,n)t^=(a[i]&1^1),sz+=(a[i]&1);
if(t||sz!=1)return t^1;
return solve()^1;
}
int main(){
scanf("%d",&n);
fp(i,1,n)scanf("%d",&a[i]);
R int t=0,sz=0;
fp(i,1,n)t^=(a[i]&1^1),sz+=(a[i]&1);
if(t||sz!=1)return puts(t?"First":"Second"),0;
puts(solve()?"First":"Second");
return 0;
}
\(E\)
模型日常不会转化
首先我们假设\(A\)已经把序列重新排列好了,那么\(B\)要怎么做才能使得字典序最大
容易发现如果两个数不互质,那么这两个数的相对顺序是不变的,另一方面,如果新的序列和原来序列每两个不互质的数相对位置不变,证明这个新的序列合法
那么如果\(i<j\)且\(\gcd(a_i,a_j)\neq 1\),我们就从\(i\)向\(j\)连一条边,最后只要求这个图字典序最大的拓扑序就可以了。鉴于这个图有着如果\(a_i=a_j\)则\(i,j\)有相同的出边的优秀性质,所以我们可以直接贪心的跑出最大的字典序
然后来考虑\(A\)在这种情况下该怎么卡,先把之前连的边变成无向边,然后我们需要对所有边定向。首先各个连通块之间是独立的,因为对于两个连通块形成的序列,\(B\)一定可以把这两个序列合并成字典序最大的而不改变每个序列里的相对顺序。
所以考虑单独一个联通快该怎么卡,首先我们找到这个连通块中最小的点\(u\),并以\(u\)为根节点随便跑一棵生成树,那么我们发现这样的话这个连通块中\(B\)能选择的第一个点只能是\(u\)了。那么我们继续考虑\(A\)如何让之后的也尽量优,\(A\)可以删掉点\(u\),然后从小到大枚举它的每一个出点,并对每一个出点也执行这个过程。我们发现最后会形成一棵\(dfs\)生成树,而且这个\(dfs\)生成树就是最优的,那么其它边实际上并不会影响最终的序列了
然后没有然后了
//quming
#include<bits/stdc++.h>
#define R register
#define pb push_back
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=2005+5,M=5e6+5;
struct eg{int v,nx;}e[M];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
vector<int>to[N];int deg[N],vis[N],a[N],n;
priority_queue<int>q;
void dfs(int u){
vis[u]=1;
for(auto v:to[u])if(!vis[v])add(u,v),++deg[v],dfs(v);
}
int main(){
scanf("%d",&n);
fp(i,1,n)scanf("%d",&a[i]);
sort(a+1,a+1+n);
fp(i,1,n)fp(j,i+1,n)if(__gcd(a[i],a[j])>1)to[i].pb(j),to[j].pb(i);
fp(i,1,n)if(!vis[i])dfs(i);
fp(i,1,n)if(!deg[i])q.push(i);
for(R int u;!q.empty();){
u=q.top(),q.pop();
printf("%d ",a[u]);
go(u)if(!--deg[v])q.push(v);
}
return 0;
}
\(F\)
我为什么总是想了一点点就停止了……明明再想下去就能出来了……
首先我们枚举初始时棋子放在那个点上,然后以这个点为根\(dfs\)
对于一个点,定义\(f_u\)表示游戏只在\(u\)的这个子树中(即棋子不会出去),且初始时棋子在\(u\),那么先手必胜还是必败
那么最后\(f_{rt}\)就是根节点的答案
先给出结论:
\(1.\)若存在点\(v\)满足\(a_u>a_v\)且\(v\)子树中先手必败,则\(u\)先手必胜
\(2.\)否则\(u\)先手必败(或者\(u\)是叶子)
先证明第一个,首先\(a_u>a_v\)证明\(a_u\neq 0\),那么肯定可以下去,而此时如果后手想从\(v\)回来,\(u\)可以又让它下去。因为\(a_u>a_v\),所以如果一直这么秦王绕柱走的话最后肯定先手必胜,所以\(v\)肯定不会回来了,也就是说接下来的游戏的确只在\(v\)的子树中。然而万一后手的目的是通过这样秦王绕柱走来减小\(a_v\)呢?我们发现及时\(a_v\)减小,\(v\)子树内的必败也不会变为必胜,所以并没有影响
关于第二个的证明,首先叶子不用说了,否则假设\(u\)到了\(v\),那么必有\(v\)子树必胜或者\(a_u\leq a_v\),如果是后者那么后手跟先手秦王绕柱走,先手必死。如果是前者后手按子树里的必胜策略走就是,还是必胜
那么直接\(O(n^2)\)就可以了
\(ps:\)鉴于题解里那一句挑(调)衅(戏)的话,顺便写了一下\(O(n)\)的,就是在\(O(n^2)\)的基础上加个换根就行了
这里是\(O(n^2)\)的
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=5005;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int f[N],a[N],n;
void dfs(int u,int fa){
f[u]=0;
go(u)if(v!=fa&&a[u]>a[v])dfs(v,u),f[u]|=f[v]^1;
}
int main(){
scanf("%d",&n);
fp(i,1,n)scanf("%d",&a[i]);
for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
fp(i,1,n)dfs(i,0);
fp(i,1,n)if(f[i])printf("%d ",i);
return 0;
}
这里是\(O(n)\)的
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=5005;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int ans[N],f[N],a[N],sz[N],n;
void dfs1(int u,int fa){
f[u]=0;
go(u)if(v!=fa){
dfs1(v,u),++sz[u];
if(a[u]>a[v])f[u]|=!f[v];
}
}
void dfs2(int u,int fa,int pa){
pa=(a[u]>a[fa]&&!pa);
ans[u]=f[u]|pa;
vector<int>st(sz[u]+2),Pre(sz[u]+2),suf(sz[u]+2);
int top=0;
go(u)if(v!=fa)st[++top]=v;
Pre[0]=0;fp(i,1,top)Pre[i]=Pre[i-1]|(a[u]>a[st[i]]&&!f[st[i]]);
suf[top+1]=0;fd(i,top,1)suf[i]=suf[i+1]|(a[u]>a[st[i]]&&!f[st[i]]);
fp(i,1,top)dfs2(st[i],u,Pre[i-1]|suf[i+1]|pa);
}
int main(){
scanf("%d",&n);
fp(i,1,n)scanf("%d",&a[i]);
for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
dfs1(1,0),dfs2(1,0,1);
fp(i,1,n)if(ans[i])printf("%d ",i);
return 0;
}
AtCoder Grand Contest 010题解的更多相关文章
- AtCoder Grand Contest 010
AtCoder Grand Contest 010 A - Addition 翻译 黑板上写了\(n\)个正整数,每次会擦去两个奇偶性相同的数,然后把他们的和写会到黑板上,问最终能否只剩下一个数. 题 ...
- Atcoder Grand Contest 010 C - Cleaning 树贪心(伪)
C - Cleaning 题目连接: http://agc010.contest.atcoder.jp/tasks/agc010_c Description There is a tree with ...
- Atcoder Grand Contest 010 B - Boxes 差分
B - Boxes 题目连接: http://agc010.contest.atcoder.jp/tasks/agc010_b Description There are N boxes arrang ...
- AtCoder Grand Contest 010 C:Cleaning
题目传送门:https://agc010.contest.atcoder.jp/tasks/agc010_c 题目翻译 给你一棵树,每个点有个权值,每次操作可以选择两个度数为\(1\)的结点,然后让这 ...
- AtCoder Grand Contest 017 题解
A - Biscuits 题目: 给出 \(n\) 个物品,每个物品有一个权值. 问有多少种选取方式使得物品权值之和 \(\bmod\space 2\) 为 \(p\). \(n \leq 50\) ...
- AtCoder Grand Contest 010 F - Tree Game
题目传送门:https://agc010.contest.atcoder.jp/tasks/agc010_f 题目大意: 给定一棵树,每个节点上有\(a_i\)个石子,某个节点上有一个棋子,两人轮流操 ...
- Atcoder Grand Contest 054 题解
那天晚上由于毕业晚会与同学吃饭喝酒没打 AGC,第二天稍微补了下题,目前补到了 E,显然 AGC 的 F 对于我来说都是不可做题就没补了(bushi A 简单题,不难发现如果我们通过三次及以上的操作将 ...
- AtCoder Grand Contest 030题解
第一次套刷AtCoder 体验良好 传送门 Poisonous Cookies cout<<b+min(c,a+b+); Tree Burning 难度跨度有点大啊 可以证明当第一次转向之 ...
- AtCoder Grand Contest 031题解
题面 传送门 题解 比赛的之后做完\(AB\)就开始发呆了--简直菜的一笔啊-- \(A - Colorful\ Subsequence\) 如果第\(i\)个字母选,那么它前面任意一个别的字母的选择 ...
随机推荐
- STL之 stack的基础应用
头文件 #include<stack> stack<int> s; stack<char> s;//定义一个名字为s 的存int char的stack 基本指令 ...
- java mybatis
mybatis简单使用记录一下 mybatis官网:http://www.mybatis.org/mybatis-3/ 参考博客:https://blog.csdn.net/iku5200/artic ...
- c# 基于RTMP推流 PC+移动端 拉流播放
网上关于直播相关的文章很多,但是讲解还是不够系统,对于刚刚接触直播开发的朋友实施起来会浪费不少时间.下面结合我自己的经验, 介绍一下直播方面的实战经验. 分成两个部分 第一部分是标题中介绍的基于RTM ...
- 图解Apache Mina
Apache MINA 是一个用于简化开发构建高性能.高可扩展的网络应用框架.通过JAVA NIO在各种传输协议(如:TCP/IP.UDP/IP)上提供抽象的事件驱动异步API Apache MINA ...
- 在线生成二维码API接口
1.http://s.jiathis.com/qrcode.php?url=kk 2.http://qr.liantu.com/api.php?text=kk 3.http://api.k780.co ...
- Spring WebSocket中403错误解决
最近测试了一下spring的websocket,遇到了一个比较恶心的问题,在这记录一下. 问题源自之前开发的一个h5项目,这个项目在80端口下一直放着,就顺便在里面随便加了几行代码测试websocke ...
- excel将一个工作表根据条件拆分成多个sheet工作表与合并多个sheet工作表
本例介绍在excel中如何将一个工作表根据条件拆分成多个工作表. 注意:很多朋友反映sheets(i).delete这句代码出错,要注意下面第一个步骤,要拆分的数据工作表名称为“数据源”, 而不是你新 ...
- Optaplanner与Google OR-Tools的区别
在规划相关的项目工作中,近两年我们的项目主要使用的是Optaplanner作为规划引擎,其核心也是一个的规划求解器(Solver).但作为另一个著名开源求解器Google OR-Tools(下称OR- ...
- 零基础如何学好Python 之int 数字整型类型 定义int()范围大小转换
本文主题是讲python数字类型python int整型使用方法及技巧.它是不可变数据类型中的一种,它的一些性质和字符串是一样的,注意是整型不是整形哦. Python int有多种数字类型:整型int ...
- 《奋斗吧!菜鸟》 第九次作业:Beta冲刺 Scrum meeting 1
项目 内容 这个作业属于哪个课程 任课教师链接 作业要求 https://www.cnblogs.com/nwnu-daizh/p/11056511.html 团队名称 奋斗吧!菜鸟 作业学习目标 掌 ...