AtCoder Grand Contest 014

AtCoder Grand Contest 014

有三我的,分別有\(A,B,C\)塊餅乾,每次每一個人都會把本身的餅乾分紅相等的兩份而後給其餘兩我的。當其中有一我的的餅乾數量是奇數的時候中止,求會進行幾回這樣子的操做,或者會永遠進行下去。ios

首先無解的狀況必定是三個數都是相等的偶數。cookie

不然直接暴力模擬就好了。(盲猜答案不會很大)spa

證實一下答案的範圍:不妨令\(A\le B\le C\),那麼最大值和最小值之間的差就是\(C-A\),那麼執行完一次操做以後最大值和最小值的差變成了\(\frac{C-A}{2}\),這樣子以來就能夠證實執行次數不會超過\(log\)次了。.net

#include<iostream>
#include<cstdio>
using namespace std;
int a,b,c,ans;
int main()
{
    scanf("%d%d%d",&a,&b,&c);
    if(a%2==0&&a==b&&b==c){puts("-1");return 0;}
    while(a%2==0&&b%2==0&&c%2==0)
    {
        int aa=a>>1,bb=b>>1,cc=c>>1;
        a=bb+cc;b=aa+cc;c=aa+bb;++ans;
    }
    printf("%d\n",ans);
    return 0;
}

B - Unplanned Queries

有一棵樹,一開始全部邊權都是\(0\),執行\(M\)次操做,每次會把\(a_i\)\(b_i\)路徑上的每一條邊的邊權所有加\(1\),切最終每一條邊的邊權都是偶數。判斷這樣一棵樹是否存在。code

對於全部操做隨便構建一棵生成樹出來進行一下判斷就好了。blog

這樣對的緣由是由於你隨便怎麼連對於奇偶性是不改變的。排序

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 100100
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,m,f[MAX],a[MAX];
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
vector<int> E[MAX];
void dfs(int u,int ff){for(int v:E[u])if(v!=ff)dfs(v,u),a[u]^=a[v];}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)f[i]=i;
    for(int i=1;i<=m;++i)
    {
        int u=read(),v=read();
        if(getf(u)!=getf(v))
            E[u].push_back(v),E[v].push_back(u),f[getf(u)]=getf(v);
        a[u]^=1;a[v]^=1;
    }
    for(int i=1;i<=n;++i)
        if(getf(i)!=getf(1))E[1].push_back(i),E[i].push_back(1),f[getf(i)]=getf(1); dfs(1,0);
    for(int i=1;i<=n;++i)if(a[i]){puts("NO");return 0;}
    puts("YES");
    return 0;
}

C - Closed Rooms

有一個\(H\times W\)的網格,有些格子不能走。如今有一我的從某個起點開始,進行如下步驟:首先沿着相鄰格子走不超過\(k\)步,而後選擇不超過\(k\)個不能走的格子讓它們變成能走。get

問最少進行上述操做多少次這我的能夠走到網格圖的邊界位置。string

把操做順序換一下,咱們先走\(k\)次。接下來進行若干輪,每次都是先把\(k\)個格子解鎖再走,等價於沒有限制了。it

因此只須要預處理先走\(k\)步能夠到達的位置再計算一下最短路就行啦。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
#define MAX 808
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int ans,n,m,K,sx,sy;
char g[MAX][MAX];
int Calc(int x,int y){return min(min(x-1,n-x),min(y-1,m-y));}
int dis[MAX][MAX];
int d[4][2]={1,0,0,1,-1,0,0,-1};
void BFS(int sx,int sy)
{
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)dis[i][j]=1e9;
    dis[sx][sy]=0;ans=1e9;
    queue<int> Qx,Qy;Qx.push(sx),Qy.push(sy);
    while(!Qx.empty())
    {
        int x=Qx.front(),y=Qy.front();Qx.pop();Qy.pop();
        ans=min(ans,(Calc(x,y)+K-1)/K);
        if(dis[x][y]>=K)continue;
        for(int i=0;i<4;++i)
        {
            int xx=x+d[i][0],yy=y+d[i][1];
            if(xx<1||xx>n||yy<1||yy>m)continue;
            if(g[xx][yy]=='#')continue;
            if(dis[xx][yy]<=dis[x][y]+1)continue;
            dis[xx][yy]=dis[x][y]+1;
            Qx.push(xx);Qy.push(yy);
        }
    }
}
int main()
{
    n=read();m=read();K=read();
    for(int i=1;i<=n;++i)scanf("%s",g[i]+1);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(g[i][j]=='S')sx=i,sy=j;
    BFS(sx,sy);
    printf("%d\n",ans+1);
    return 0;
}

D - Black and White Tree

有一棵樹,兩我的輪流給節點染色,先手染白,後手染黑。

染完以後把全部和黑色節點相鄰的點所有染黑。

若是還有白點則先手勝,不然後手勝。

判斷勝負狀況。

首先發現勝利狀況是存在一個白點使得其相鄰的點全是白點。

那麼,發現若是這棵樹存在一個完美匹配的話,那麼後手必勝,即先手不管走哪一個位置,後手必定能夠走一個匹配的相鄰的位置。

不然的話找一個不在匹配內的點做爲根節點,這樣子它的全部兒子的匹配都在其自身的子樹內,這樣子直接對於全部兒子染色,此時後手必須染其兒子對應的匹配,此時先手必勝。

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 100100
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n;
vector<int> E[MAX];
bool dfs(int u,int ff)
{
    int cnt=0;
    for(int v:E[u])
    {
        if(v==ff)continue;
        if(dfs(v,u))++cnt;
    }
    if(cnt>=2){puts("First");exit(0);}
    return cnt^1;
}
int main()
{
    n=read();
    for(int i=1;i<n;++i)
    {
        int u=read(),v=read();
        E[u].push_back(v);
        E[v].push_back(u);
    }
    if(dfs(1,0))puts("First");
    else puts("Second");
    return 0;
}

E - Blue and Red Tree

你有一棵樹,一開始全部邊都是藍色的。

你要執行\(n-1\)次以下的操做:每次選定一條全是藍色邊的路徑,隨意斷掉其中一條邊,而後在兩個端點之間連上一條紅邊。

如今給你由紅邊組成的樹,問你可否從藍邊樹變成給定的紅邊樹。

發現最後一次操做的邊必定既在藍樹中又在紅樹中出現,而連完邊以後這兩個點就能夠直接合並在一塊兒。

那麼咱們每次找到這樣一條邊,而後把兩個集合合併。

那麼直接啓發式合併處理這個問題就好了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define MAX 100100
#define pi pair<int,int>
#define mp make_pair
#define fr first
#define sd second
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,f[MAX];
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
queue<pi> Q;
map<pi,int> M;
set<int> E[MAX];
pi get(int x,int y){if(x>y)swap(x,y);return mp(x,y);}
void Link(int x,int y)
{
    E[x].insert(y);E[y].insert(x);
    pi u=get(x,y);
    if(++M[u]==2)Q.push(u);
}
int main()
{
    n=read();
    for(int i=1;i<=n;++i)f[i]=i;
    for(int i=1;i<=n+n-2;++i)Link(read(),read());
    for(int i=1,x,y;i<n;++i)
    {
        do
        {
            if(Q.empty()){puts("NO");return 0;}
            x=Q.front().fr,y=Q.front().sd;Q.pop();
            x=getf(x);y=getf(y);
        }while(x==y);
        if(E[x].size()>E[y].size())swap(x,y);
        f[x]=y;M.erase(get(x,y));E[y].erase(x);
        for(auto v:E[x])
        {
            int fv=getf(v);
            if(fv==y)continue;
            M.erase(get(x,fv));
            E[fv].erase(x);
            Link(fv,y);
        }
    }
    puts("YES");
    return 0;
}

F - Strange Sorting

給定一個排列,把全部是前綴最大值的數直接丟到數列的最後,問多少次這樣的操做以後數列會被排好序。

不會.jpg。因此能夠開心的照抄___的題解了

首先發現\(1\)這個東西頗有意思,它並不影響其餘元素,即若是\(1\)在開頭位置那麼就沒它的事了,不然它必定不會成爲前綴最大值。那麼咱們把\(1\)給丟掉,只考慮\([2,n]\)的數量,僞裝它的答案是\(T\),那麼最終的答案就是\(T\)或者\(T+1\)。首先\(T=0\)的狀況特殊處理掉。不然的話考慮進行完\(T-1\)次以後的數列,設\(f\)爲數列的第一項,那麼\(f>2\),由於若是\(f=2\)的話證實這個序列要麼已經排好序了,要麼在下一次操做以後\(2\)會被丟到後面,又由於沒有排好序,因此確定不能只進行一次就獲得最終序列。

這樣子的話,進行完下一次操做以後\([2,n]\)就完成了排序,考慮把\(1\)給加入進來,若是數列前三項是\(f,1,2\),那麼顯然一次操做以後\(1\)也順帶回到了本身的位置,此時答案仍然是\(T\)。不然的話,一次操做以後顯然\(1\)尚未歸位,須要再進行一次\(1\)才能回到本身的位置上,此時答案是\(T+1\)

而後我就不會證了,抄遍代碼就滾粗了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 200200
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,a[MAX],b[MAX],T[MAX],f[MAX];
int main()
{
    n=read();
    for(int i=1;i<=n;++i)b[a[i]=read()]=i;
    for(int i=n-1;i;--i)
        if(!T[i+1])
        {
            if(b[i]>b[i+1])T[i]=1,f[i]=i+1;
            else T[i]=0;
        }
        else
        {
            if((b[f[i+1]]<b[i])+(b[i]<b[i+1])+(b[i+1]<b[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;
}
相關文章
相關標籤/搜索