AtCoder Grand Contest 014题解
\(A\)
首先大力猜测一下答案不会很大,所以次数大于\(10^6\)输出\(-1\)就行了
不过我并不会证上界,据说是因为如果\(a=b=c\)且都是偶数肯定\(-1\),否则设\(a\leq b\leq c\),则最大最小值的差为\(c-a\),一次操作之后变成了\({c-a\over 2}\),所以操作次数就是\(\log\)级别的了
typedef long long ll;
ll a,b,c,sum;int res;
int main(){
scanf("%lld%lld%lld",&a,&b,&c);
sum=a+b+c;
while(true){
if((a&1)|(b&1)|(c&1))return printf("%d\n",res),0;
a=(sum-a)>>1,b=(sum-b)>>1,c=(sum-c)>>1;
if(++res>1e6)return puts("-1"),0;
}
return 0;
}
\(B\)
既然题目要求每条边被覆盖偶数次,那么一定可以被拆分成若干个联通边集且这个边集里每条边都被覆盖\(2\)次,而这个边集肯定形如若干条\((a_1,a_2),(a_2,a_3),...,(a_n,a_1)\)的路径的并,再转化一下就是在新图中将\((u,v)\)连边,且新图可以被拆分成若干条欧拉回路。那么充要条件就是每个点度数都为偶数
//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=2e5+5;
int d[N],n,m;
int main(){
scanf("%d%d",&n,&m);
for(R int i=1,u,v;i<=m;++i)scanf("%d%d",&u,&v),++d[u],++d[v];
fp(i,1,n)if(d[i]&1)return puts("NO"),0;
puts("YES");
return 0;
}
\(C\)
肯定是先随便走到一个点,然后消走消走的就可以直接往一个方向冲了,那么\(bfs\)找出所有一开始能到的点,然后看看从哪个点开始冲就是了
//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=1005;
const int dx[]={0,1,0,-1};
const int dy[]={1,0,-1,0};
char mp[N][N];int vis[N][N],dis[N][N],qx[N*N],qy[N*N],n,m,k,res,sx,sy;
inline int calc(R int x,R int y){
cmin(res,(x-1+k-1)/k),cmin(res,(n-x+k-1)/k);
cmin(res,(y-1+k-1)/k),cmin(res,(m-y+k-1)/k);
}
void bfs(){
int h=1,t=0,x,y,tx,ty;qx[++t]=sx,qy[t]=sy,vis[sx][sy]=1,dis[sx][sy]=0;
while(h<=t){
x=qx[h],y=qy[h++];
if(dis[x][y]==k)continue;
fp(i,0,3){
tx=x+dx[i],ty=y+dy[i];
if(!vis[tx][ty]&&tx>=1&&tx<=n&&ty>=1&&ty<=m)
++t,qx[t]=tx,qy[t]=ty,vis[tx][ty]=1,dis[tx][ty]=dis[x][y]+1;
}
}
fp(i,1,t)calc(qx[i],qy[i]);
}
int main(){
scanf("%d%d%d",&n,&m,&k),res=2333333;
fp(i,1,n){
scanf("%s",mp[i]+1);
fp(j,1,m){
if(mp[i][j]=='S')sx=i,sy=j;
if(mp[i][j]=='#')vis[i][j]=1;
}
}
bfs();
printf("%d\n",res+1);
return 0;
}
\(D\)
显然曲明最擅长的就是猜测出假的结论
首先给出结论:后手必胜当且仅当这棵树有完美匹配
这棵树有完美匹配的时候,因为先手染什么颜色后手只要染它的匹配,就能保证每个白色节点有至少一个与它相邻的黑色节点了
如果一个点连着两个以上的叶子必定是先手必胜了,所以接下来我们默认所有的点连着的叶子个数小于等于\(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=2e5+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;}
int dep[N],vis[N],fa[N],n;
void dfs(int u,int fa){
go(u)if(v!=fa)dfs(v,u);
if(!vis[u]&&vis[fa]){puts("First");exit(0);}
if(!vis[u])vis[u]=vis[fa]=1;
}
int main(){
// freopen("testdata.in","r",stdin);
scanf("%d",&n);
for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
vis[0]=1;dfs(1,0);
puts("Second");
return 0;
}
\(E\)
容易发现,一次操作合法,当且仅当删掉蓝边和删掉红边之后两边点集是一样的
那么我们考虑倒着加边,则如果两个连通块之间多于\(1\)条边,那么必定是一条红边一条蓝边,且这个操作可以进行,那么我们把这两个连通块缩成一个点就好了
关于维护连通块直接的边集,可以用\(multiset\),然后启发式合并就行了
//quming
#include<bits/stdc++.h>
#define R register
#define fi first
#define se second
#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 pair<int,int> pi;
const int N=2e5+5;
multiset<int>to[N];map<pi,int>mp;queue<pi>q;int n;
inline void ins(R int u,R int v){
if(u==v)return;
if(u>v)swap(u,v);
++mp[pi(u,v)],to[u].insert(v),to[v].insert(u);
if(mp[pi(u,v)]==2)q.push(pi(u,v));
}
inline void del(R int u,R int v){
if(u>v)swap(u,v);
if(mp.count(pi(u,v)))mp.erase(pi(u,v));
}
inline void merge(R int u,R int v){
for(auto w:to[v])del(w,v),to[w].erase(to[w].find(v)),ins(w,u);
}
int main(){
scanf("%d",&n);
for(R int i=1,u,v;i<=n*2-2;++i)scanf("%d%d",&u,&v),ins(u,v);
fp(i,1,n-1){
while(233){
if(q.empty())return puts("NO"),0;
R pi t=q.front();q.pop();
if(mp.count(t)){
if(to[t.fi].size()<to[t.se].size())swap(t.fi,t.se);
merge(t.fi,t.se);
break;
}
}
}
puts("YES");
return 0;
}
\(F\)
好神仙的题啊……
这篇题解比起其他题解的优势是翻译的比较全
首先我们发现,如果去掉\(1\)是不会改变每个元素是\(low\)还是\(high\)的,那么我们去掉\(1\),假设需要\(T\)次令\(2\)到\(n\)有序,如果此时\(1\)已经在最前面了,那么答案就是\(T\),否则是\(T+1\)
如果\(T=0\),那么答案是\(T\)还是\(T+1\)很好判断。否则我们考虑进行了\(T-1\)次操作之后的序列,令\(f\)是\(2\)到\(n\)中排在最前面的元素,易知\(f>2\)(如果\(f=2\)的话,可以用反证法证明,要么此时\(2\)到\(n\)已经有序,要么再进行一次操作之后仍不有序)。然后我们发现,当且仅当此时三个数的相对位置形如\((f,1,2)\)时,答案为\(T\),否则为\(T+1\)
然后我们要证明这三个数在环上的相对位置是不变的,也就是说如果初始时相对位置为\((1,2,f)\),那么变化过程中相对位置只会有\((1,2,f),(2,f,1),(f,1,2)\)三种情况
先来证明另一件事,就是\(f\)如果不是在\(2\)到\(n\)中所有数的最前面,它必定是一个\(low\)元素。这一段中考虑的元素只有\(2\)到\(n\)。考虑反证法,假设\(x\)是一个不在第一位的\(high\)元素,因为第一位必定是\(high\),所以一次操作之后必定存在一个\(y\)使得\(y<x\)且\(y\)在\(x\)前面,接下来的操作过程中,如果\(y,x\)类型相同它们必定同时移动,而它们分开当且仅当某个时刻\(y\)是\(low\)而\(x\)是\(high\),不过因为此时\(x\)依然不是第一个元素,所以又陷入了之前那种情况。所以可以发现如果\(f\)是一个不在最前面的\(high\)元素它无论如何都不可能在\(T-1\)次操作后到达最前面,所以上面那个结论成立
然后开始分类讨论了
- 如果\(1\)在第一个
- 如果\(2\)在第二个,那么\(1,2\)是\(high\)而\(f\)是\(low\),环不会变
- 如果\(f\)是第二个,那么\(1,f\)是\(high\)而\(2\)是\(low\),环不会变
- 否则\(1\)是\(high\),\(2,f\)是\(low\),环不会变
- 如果\(2\)是第一个,那么\(2\)是\(high\)而\(1,f\)是\(low\),环不会变
- 如果\(f\)是第一个,那么\(f\)是\(high\)而\(1,2\)是\(low\),环不会变
- 否则\(1,2,f\)都是\(low\),环不会变
然后我们来倒推,设\(T_i\)表示只考虑\(i\)到\(n\)的元素时的最小次数,\(f_i\)表示此时对应的\(f\),如果\(T_i=0\)则\(f_i\)未定义。初值为\(T_1=0\),答案为\(T_1\)
我们设\(q_i\)表示\(i\)的位置,即\(p_{q_i}=i\),然后递推过程如下
- 如果\(T_{i+1}=0\)
- 如果\(q_i>q_{i+1}\),则\(T_i=1,f_i=i+1\)
- 否则\(T_i=0\)
- 否则
- 如果三个元素的环按照\(q_{f_{i+1}},q_i,q_{i+1}\)的顺序,则\(T_i=T_{i+1},f_i=f_{i+1}\)
- 否则\(T_i=T_{i+1}+1,f_i=i+1\)
时间复杂度\(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=2e5+5;
int p[N],q[N],f[N],t[N],n;
int main(){
scanf("%d",&n);
fp(i,1,n)scanf("%d",&p[i]),q[p[i]]=i;
t[n]=0;
fd(i,n-1,1)
if(!t[i+1])q[i]>q[i+1]?(t[i]=1,f[i]=i+1):t[i]=0;
else{
if((q[f[i+1]]<q[i])+(q[i]<q[i+1])+(q[i+1]<q[f[i+1]])==2)
t[i]=t[i+1],f[i]=f[i+1];
else t[i]=t[i+1]+1,f[i]=i+1;
}
printf("%d\n",t[1]);
return 0;
}
AtCoder Grand Contest 014题解的更多相关文章
- AtCoder Grand Contest 014 题解
A - Cookie Exchanges 模拟 Problem Statement Takahashi, Aoki and Snuke love cookies. They have A, B and ...
- AtCoder Grand Contest 014
AtCoder Grand Contest 014 A - Cookie Exchanges 有三个人,分别有\(A,B,C\)块饼干,每次每个人都会把自己的饼干分成相等的两份然后给其他两个人.当其中 ...
- AtCoder Grand Contest 014 D:Black and White Tree
题目传送门:https://agc014.contest.atcoder.jp/tasks/agc014_d 题目翻译 给你一棵树,每次任选一个点染色,先手染白色,后手染黑色.如果最后存在一个白色的点 ...
- AtCoder Grand Contest 014 E:Blue and Red Tree
题目传送门:https://agc014.contest.atcoder.jp/tasks/agc014_e 题目翻译 有一棵有\(N\)个点的树,初始时每条边都是蓝色的,每次你可以选择一条由蓝色边构 ...
- AtCoder Grand Contest 017 题解
A - Biscuits 题目: 给出 \(n\) 个物品,每个物品有一个权值. 问有多少种选取方式使得物品权值之和 \(\bmod\space 2\) 为 \(p\). \(n \leq 50\) ...
- 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\)个字母选,那么它前面任意一个别的字母的选择 ...
- AtCoder Grand Contest 039 题解
传送门 \(A\) 首先只有一串的情况下,遇到相同的肯定是改后面那一个最优,然后两串的话可能要分奇偶讨论一下 //quming #include<bits/stdc++.h> #defin ...
随机推荐
- 缓存的设计及PHP实现LFU
1. 恒定缓存性能有哪些因素? 命中率.缓存更新策略.缓存最大数据量. 命中率:指请求缓存次数和缓存返回正确结果次数的比例.比例越高,缓存的使用率越高,用来衡量缓存机智的好坏和效率.如果数据频繁更新, ...
- JDBC使用8.0驱动包连接mysql设置时区serverTimezone
驱动包用的是新版 mysql-connector-java-8.0.16.jar新版的驱动类改成了com.mysql.cj.jdbc.Driver新版驱动连接url也有所改动I.指定时区 如果不设置时 ...
- VisualStudio 连接 MySql 实现增删查改
首先创建数据库,建立一个用户登录表 2.visualStudio默认是不支持MySql的,要想通过Ado.Net 操作MySql 需要在管理NeGet包添加对MySql.Data 和 MySql.D ...
- nginx-rtmp之直播视频流的推送
一.RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写.该协议基于TCP,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变 ...
- 你有自信写while(true)吗?
每次写while(true)的时候会不会心虚? 特别逻辑稍微复杂一点
- C++ | 使用const std::map,map::[]时遇到的一个bug
原函数简化后如下: void fun(const map<int,vector<int>> &mp, int index) { for (auto tmp : mp[i ...
- Hibernate中Session.get()/load()之区别
原文链接http://sunxin1001.iteye.com/blog/292090 Session.load/get方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象.其区 ...
- 如何把前端用ajax发过来的图片传到node上,并且用node保存在oss图片服务器上?
一:只上传一张图片 1.1:node需要安装的插件,先安好 npm install ali-oss uuid co --save A.ali-oss 用途:aliyun OSS(Object Stor ...
- git 命令删除文件操作
在github上只能删除仓库,却无法删除文件夹或文件, 所以只能通过命令来解决 1.添加文件并提交命令 2.推送到git服务器命令 3.删除文件并提交命令 4.推送到git服务器 查看下git 是否存 ...
- centos7开启80端口及其他端口
首先centos7的防火墙由iptables改为了firewalld 1. 执行命令:firewall-cmd --zone=public --add-port=80/tcp --permanent ...