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;
}

原文地址:https://www.cnblogs.com/yuanquming/p/11551888.html

时间: 2024-08-30 13:54:46

AtCoder Grand Contest 014题解的相关文章

AtCoder Grand Contest 014

AtCoder Grand Contest 014 A - Cookie Exchanges 有三个人,分别有\(A,B,C\)块饼干,每次每个人都会把自己的饼干分成相等的两份然后给其他两个人.当其中有一个人的饼干数量是奇数的时候停止,求会进行几次这样子的操作,或者会永远进行下去. 首先无解的情况一定是三个数都是相等的偶数. 否则直接暴力模拟就行了.(盲猜答案不会很大) 证明一下答案的范围:不妨令\(A\le B\le C\),那么最大值和最小值之间的差就是\(C-A\),那么执行完一次操作之后

AtCoder Grand Contest 014 E:Blue and Red Tree

题目传送门:https://agc014.contest.atcoder.jp/tasks/agc014_e 题目翻译 有一棵有\(N\)个点的树,初始时每条边都是蓝色的,每次你可以选择一条由蓝色边构成的简单路径,让这条路径的两个端点间连上一条红边,然后断开这条路径上的某条蓝边.这样做\(N-1\)次,就可以把原本的蓝树变成红树.现在给你蓝树和红树的样子,问你可不可能把给出的蓝树变成给出的红树.\(N\leqslant 10^5\) 题解 先膜一发大佬的题解:https://blog.csdn.

AtCoder Grand Contest 007题解

传送门 \(A\) 咕咕咕 //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<

AtCoder Grand Contest 008题解

传送门 \(A\) 分类讨论就行了 然而我竟然有一种讨论不动的感觉 int x,y; inline int min(R int x,R int y){return x<y?x:y;} inline int min(R int a,R int b,R int c,R int d){ return min(min(a,b),min(c,d)); } inline int calc(R int x,R int y){return y>=x?y-x:x-y+2;} int main(){ scanf(

AtCoder Grand Contest 039 题解

传送门 \(A\) 首先只有一串的情况下,遇到相同的肯定是改后面那一个最优,然后两串的话可能要分奇偶讨论一下 //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=

Atcoder Grand Contest 003 题解

A - Wanna go back home 如果有S就必须要有N,反之亦然,如果有E必须要有W,反之亦然.判断一下就好了. //waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int

AtCoder Grand Contest 008 题解

A - Simple Calculator 模拟+分类讨论即可. //waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int)((x).size())) typedef pair<

AtCoder Grand Contest 013 题解

A - Sorted Arrays 贪心,看看不下降和不上升最长能到哪,直接转移过去即可. 1 //waz 2 #include <bits/stdc++.h> 3 4 using namespace std; 5 6 #define mp make_pair 7 #define pb push_back 8 #define fi first 9 #define se second 10 #define ALL(x) (x).begin(), (x).end() 11 #define SZ(x

AtCoder Grand Contest 015 题解

A - A+...+B Problem 可以取到的值一定是一段区间.所以答案即为max-min+1 1 //waz 2 #include <bits/stdc++.h> 3 4 using namespace std; 5 6 #define mp make_pair 7 #define pb push_back 8 #define fi first 9 #define se second 10 #define ALL(x) (x).begin(), (x).end() 11 #define