1. 程式人生 > >2017國家集訓隊作業Atcoder題目試做

2017國家集訓隊作業Atcoder題目試做

http pre 發現 計數 reverse fine 代碼 register 標記

2017國家集訓隊作業Atcoder題目試做

雖然遠沒有達到這個水平,但是據說Atcoder思維難度大,代碼難度小,適合我這種不會打字的選手,所以試著做一做

不知道能做幾題啊

在完全自己做出來的題前面打"√“(目前好像還沒有誒。。。o(╥﹏╥)o)

計數器菌:4/104

### agc001_d

如果兩個字符確定相等就在中間連一條邊,那麽所有字符相同就等價於使整個圖聯通

然後發現至少要\(n-1\)條邊,而事實上一個序列貢獻的邊數最大為\(\frac n 2\)條,而且一旦序列裏有一個奇數貢獻的邊數就會減去\(\frac 1 2\),所以如果原始序列出現\(\gt 2\)個奇數,那麽就不可行

一個偶數序列,整體向左平移一個之後,正好全部連起來了

如果有奇數怎麽辦?因為至多兩個奇數,我們把奇數放到兩邊,中間全是偶數,那麽可以像剛才那樣做,兩邊的奇數這樣做也符合題意。

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)

ll read(){
    ll x=0,f=1;char c=getchar();
    while(c<‘0‘ || c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘ && c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}

const int maxn=200;
int n,m;
int a[maxn];

int main(){
#ifdef LZT
//  freopen("in","r",stdin);
#endif
    int num=0;
    n=read();m=read();
    rep(i,1,m){
        a[i]=read();
        if(a[i]&1) num++;
    }
    if(num>2){
        puts("Impossible");
        return 0;
    }
    for(int i=1;i<=m;i++)
        if(a[i]&1){
            if(a[1]&1) swap(a[i],a[m]);
            else swap(a[i],a[1]);
        }
    rep(i,1,m) cout<<a[i]<<‘ ‘;
    cout<<endl;
    a[1]++;a[m]--;
    if(a[m]==0) m--;
    if(m>=2){
        cout<<m<<endl;
        rep(i,1,m) cout<<a[i]<<‘ ‘;
        cout<<endl;
    }
    else{
        if(n<=2){
            cout<<1<<endl;
            cout<<n<<endl;
        }
        else{
            cout<<2<<endl;
            cout<<n-1<<‘ ‘<<1<<endl;
        }
    }
    return 0;
}

agc001_e

我們發現答案其實就是要求\(\sum_{i=1}^{n-1}\sum_{j=i+1}^nC_{a_i+a_j+b_i+b_j}^{a_i+a_j}\)

然後知道\(C_{a_i+a_j+b_i+b_j}^{a_i+a_j}\)實際上就是點\((-a_i,-b_i)\)走到\((a_j,b_j)\)的方案數

那麽原式等價於求點集\((-a_i,-b_i)\)到點集\((a_i,b_i)\)兩兩的方案數的和減去所有點走到他對應的對稱點的方案數(即\(i=j\)的方案數)除以2(每個方案被算了兩次)

所以dp就可以了,可以想象中建立一個超級源點連向所有的\((-a_i,-b_i)\)

和超級匯點連向所有的\((a_i,b_i)\),就可以求出方案數

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)

ll read(){
    ll x=0,f=1;char c=getchar();
    while(c<‘0‘ || c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘ && c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}

const int mod=1000000007;
const int maxn=200200;
const int maxm=2200;
int n;
int a[maxn],b[maxn];
int dp[maxm*2][maxm*2];
int flag[maxm*2][maxm*2];
int ans;

void pl(int &a,ll b){
    a=(a+b%mod)%mod;
}
void mi(int &a,ll b){
    a=a-b%mod;
    while(a<0) a+=mod;
    a=a%mod;
}

int main(){
#ifdef LZT
    freopen("in","r",stdin);
#endif
    n=read();
    rep(i,1,n) a[i]=read(),b[i]=read();
    rep(i,1,n){
        flag[2100-a[i]][2100-b[i]]++;
        flag[a[i]+2100][b[i]+2100]++;
    }
    rep(i,1,4200){
        rep(j,1,4200){
            pl(dp[i][j],dp[i-1][j]);
            pl(dp[i][j],dp[i][j-1]);
            if(flag[i][j] && i<=2100 && j<=2100) pl(dp[i][j],flag[i][j]);
            if(flag[i][j] && i>=2100 && j>=2100) pl(ans,dp[i][j]*1ll*flag[i][j]);
        }
    }
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    rep(i,0,4200){
        rep(j,0,4200){
            if(i==0 && j==0) continue;
            if(i) pl(dp[i][j],dp[i-1][j]);
            if(j) pl(dp[i][j],dp[i][j-1]);
        }
    }
    rep(i,1,n)
        mi(ans,dp[a[i]+a[i]][b[i]+b[i]]);
    ans=ans*500000004ll%mod;
    cout<<ans<<endl;
    return 0;
}

agc002_d

先考慮暴力做法,對於一組詢問\((x,y,z)\),我們暴力將邊從小到大加入圖裏,當\(x\)所在的連通塊點數加\(y\)所在連通塊點數(當\(x\)\(y\)在不同連通塊時才加)第一次\(\geq z\)時,當前邊的序號就是答案

所以答案是有單調性的,可以二分

然後每一組都二分肯定不行,所以整體二分

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)

ll read(){
    ll x=0,f=1;char c=getchar();
    while(c<‘0‘ || c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘ && c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}

const int maxn=100100;
int n,m,Q;
pii edge[maxn];
struct query
{
    int ind;
    int a,b,num;
    void re(int x){
        a=read(),b=read(),num=read();
        ind=x;
    }
} q[maxn],tmp[maxn];
int ans[maxn],fa[maxn],sz[maxn];
bool ok[maxn];
pii sta[maxn];int cnt;

inline int fp(int x){if(x==fa[x]) return x;return fp(fa[x]);}

void solve(int l,int r,int le,int ri){
    //cout<<l<<‘ ‘<<r<<‘ ‘<<le<<‘ ‘<<ri<<endl;
    if(l==r){
        rep(i,le,ri) ans[q[i].ind]=l;
        int x=edge[l].fi,y=edge[l].se;
        x=fp(x);y=fp(y);
        if(x!=y){
            if(sz[x]>sz[y])swap(x,y);
            fa[x]=y;sz[y]+=sz[x];
        }
        return;
    }
    int md=(l+r)>>1;cnt=0;
    rep(i,l,md){
        int x=edge[i].fi,y=edge[i].se;
        x=fp(x);y=fp(y);
        if(x!=y){
            if(sz[x]>sz[y]) swap(x,y);
            fa[x]=y;sz[y]+=sz[x];
            sta[++cnt]=mp(x,y);
        }
    }
    rep(i,le,ri){
        query &nw=q[i];
        int a=nw.a,b=nw.b;
        //cout<<a<<‘ ‘<<b<<endl;
        a=fp(a);b=fp(b);
        //cout<<i<<‘ ‘<<a<<‘ ‘<<b<<‘ ‘;
        int nww=0;
        if(a==b) nww=sz[a];else nww=sz[a]+sz[b];
        if(nww>=nw.num) ok[i]=1;else ok[i]=0;
        //cout<<ok[i]<<endl;
    }
    int pos=le-1;
    rep(i,le,ri)
        if(ok[i]) tmp[++pos]=q[i];
    pos=ri+1;
    rrep(i,ri,le)
        if(!ok[i]) tmp[--pos]=q[i];
    //cout<<le<<‘ ‘<<ri<<‘ ‘<<pos<<endl;
    rep(i,le,ri) q[i]=tmp[i];
    while(cnt){
        int x=sta[cnt].fi,y=sta[cnt].se;
        fa[x]=x;sz[y]-=sz[x];cnt--;
    }
    solve(l,md,le,pos-1);solve(md+1,r,pos,ri);
}

int main(){
    n=read(),m=read();
    rep(i,1,m) edge[i].fi=read(),edge[i].se=read();
    Q=read();
    rep(i,1,Q) q[i].re(i);
    rep(i,1,n) sz[i]=1,fa[i]=i;
    solve(1,m,1,Q);
    rep(i,1,Q) printf("%d\n",ans[i]);
    return 0;
}

/*
5 6
2 3
4 5
1 2
1 3
1 4
1 5
6
2 4 3
2 4 4
2 4 5
1 3 3
1 3 4
1 3 5
*/

agc002_e

真難想的博弈題

首先先想狀態

不知怎麽想到把他表示成圖形

就是我們先排序 然後把一堆石子想象成一個石子個數*1的矩形。 把矩形從高到低排列變成一個圖形。

然後操作就變成了刪掉最左邊一列或者最下面一行

技術分享圖片

假設有一個點當前在\((1,1)\),那麽每次操作他向右或者向上移動一個,不能移動者輸

那麽給每個點標記上\(o\)或者\(x\),分別表示必勝和必敗

所有最外層的角上(意會)一定都是\(x\)

然後發現當\((x+1,y+1)\)不是最外層的點的時候,\((x,y)\)\((x+1,y+1)\)的標記相同

所以算法就是先把\((1,1)\)向右上方移動直到邊界為止,然後要麽向上要麽向右,如果都是必敗那麽就是必敗,否則必勝

向上向右因為只有一個方向所以只奇偶性有關

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)

ll read(){
    ll x=0,f=1;char c=getchar();
    while(c<‘0‘ || c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘ && c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}

const int maxn=100100;
int n;
int a[maxn];

int main(){
    n=read();
    rep(i,1,n) a[i]=read();
    sort(a+1,a+n+1);
    reverse(a+1,a+n+1);
    rep(i,1,n+1){
        if(a[i]<i){
            i--;
            int num=0;
            for(int j=i+1;j<=n;j++)
                if(a[j]==i) num++;
            if(num&1){
                puts("First");
                return 0;
            }
            if((a[i]-i)&1) puts("First");
            else puts("Second");
            return 0;
        }
    }
}

2017國家集訓隊作業Atcoder題目試做