传送门

\(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题解的更多相关文章

  1. AtCoder Grand Contest 010

    AtCoder Grand Contest 010 A - Addition 翻译 黑板上写了\(n\)个正整数,每次会擦去两个奇偶性相同的数,然后把他们的和写会到黑板上,问最终能否只剩下一个数. 题 ...

  2. Atcoder Grand Contest 010 C - Cleaning 树贪心(伪)

    C - Cleaning 题目连接: http://agc010.contest.atcoder.jp/tasks/agc010_c Description There is a tree with ...

  3. Atcoder Grand Contest 010 B - Boxes 差分

    B - Boxes 题目连接: http://agc010.contest.atcoder.jp/tasks/agc010_b Description There are N boxes arrang ...

  4. AtCoder Grand Contest 010 C:Cleaning

    题目传送门:https://agc010.contest.atcoder.jp/tasks/agc010_c 题目翻译 给你一棵树,每个点有个权值,每次操作可以选择两个度数为\(1\)的结点,然后让这 ...

  5. AtCoder Grand Contest 017 题解

    A - Biscuits 题目: 给出 \(n\) 个物品,每个物品有一个权值. 问有多少种选取方式使得物品权值之和 \(\bmod\space 2\) 为 \(p\). \(n \leq 50\) ...

  6. AtCoder Grand Contest 010 F - Tree Game

    题目传送门:https://agc010.contest.atcoder.jp/tasks/agc010_f 题目大意: 给定一棵树,每个节点上有\(a_i\)个石子,某个节点上有一个棋子,两人轮流操 ...

  7. Atcoder Grand Contest 054 题解

    那天晚上由于毕业晚会与同学吃饭喝酒没打 AGC,第二天稍微补了下题,目前补到了 E,显然 AGC 的 F 对于我来说都是不可做题就没补了(bushi A 简单题,不难发现如果我们通过三次及以上的操作将 ...

  8. AtCoder Grand Contest 030题解

    第一次套刷AtCoder 体验良好 传送门 Poisonous Cookies cout<<b+min(c,a+b+); Tree Burning 难度跨度有点大啊 可以证明当第一次转向之 ...

  9. AtCoder Grand Contest 031题解

    题面 传送门 题解 比赛的之后做完\(AB\)就开始发呆了--简直菜的一笔啊-- \(A - Colorful\ Subsequence\) 如果第\(i\)个字母选,那么它前面任意一个别的字母的选择 ...

随机推荐

  1. Asp.net 代码设置兼容性视图

    一.代码中设置兼容性 <summary> 兼容性视图 </summary> <param name="myPage"></param> ...

  2. 使用mavan构建自定义项目脚手架

    首先抛出一个问题是为什么要构建自定义的脚手架,maven已经为了我么提供了很多脚手架,方便我们快速的创建一个普通java项目或者是web项目,然而在实际开发中,例如银行项目,大部分都是ssm架构,我们 ...

  3. Linux 系统中如何进入退出 vim 编辑器

    在 Linux 中,vim 编辑器是系统自带的文本编辑器,但要修改某个文本文件,可不是像 Windows 那样操作,更有新手,进入 vi 编辑器后,无法退出以致于强制关机,其实,这个vim(vi)也是 ...

  4. 【SP1716】GSS3 - Can you answer these queries III(动态DP)

    题目链接 之前用线段树写了一遍,现在用\(ddp\)再写一遍. #include <cstdio> #define lc (now << 1) #define rc (now ...

  5. Node学习之(第二章:http模块)

    前言 继续上一节的探讨,今天我们来聊聊Node中怎么搭建一个简单的web服务器.平时大家在撸码的过程中,经常需要向服务器发送请求,然后服务器接受请求,响应数据.今天我们就来自己手写一个简单服务器,根据 ...

  6. Vue2.0的核心思想

    Vue的核心思想为数据驱动和组件化. 一.数据驱动——双向绑定 Vue是一种MVVM框架.而DOM是数据的一个种自然映射.传统的模式是通过Ajax请求从model请求数据,然后手动的触发DOM传入数据 ...

  7. Java 之 Servlet 基础入门

    Servlet 一.什么是 Servlet 1.概念 Servlet:server applet,是指运行在服务器端的小程序 2.Servlet   servlet 就是一个接口,定义了 Java 类 ...

  8. IDEA 显示Run Dashboard窗口的2种方式

    前言:在基于spring boot构建spring cloud微服务架构的时候,一般需要启动多个应用程序,在idea开发工具中,多个同时启动的应用可以在Run Dashboard运行仪表盘中得到更好的 ...

  9. [lambda] newbies of haskell

    site: https://www.haskell.org/ tutorial: http://learnyouahaskell.com/chapters 只言片语 Recursion is impo ...

  10. JSONObject对象

    1.JSONObject介绍 JSONObject-lib包是一个beans,collections,maps,java arrays和xml和JSON互相转换的包. 方法: 的getString() ...