1. 程式人生 > >AtCoder Grand Contest 012 D:Colorful Balls

AtCoder Grand Contest 012 D:Colorful Balls

題目傳送門:https://agc012.contest.atcoder.jp/tasks/agc012_d

題目翻譯

給你一排一共\(N\)個球,每個球有一個顏色\(c_i\)和一個重量\(w_i\),如果兩個球顏色相同,重量相加不超過\(x\)那我就可以交換這倆個球的位置。如果兩個球顏色不同,重量相加不超過\(y\)那我也可以交換這倆球的位置。你可以隨心所欲的交換或者不交換,求最後顏色序列的種數。\(N\leqslant 2×10^5;c_i\leqslant N;w_i,x,y\leqslant10^9\)

題解

首先,如果\(a,b\)可以交換,\(b,c\)可以交換,那麼\(a,c\)也可以直接交換。

對於顏色相同,我們把每個可以和當前顏色重量最小的球交換位置的球和當前顏色重量最小的球連一條邊。那麼對於可以以最小值為媒介交換的球也在一個聯通塊內了。

對於顏色不同,假設當前球和重量最小的球也顏色不同,那就把當前球和重量最小的那個球連一條邊,否則就把當前球與跟重量最小的球顏色不同的球當中重量最小的球連邊。如果不存在這個球,那就不連。

然後答案就是:
\[ \sum\limits_{i=1}^{m}\frac{cnt_i!}{\prod\limits_{j=1}^{n}sum_j!} \]
\(m\)是聯通塊個數,\(cnt_i\)表示第\(i\)個聯通塊大小,\(sum_j\)表示在第\(i\)

個聯通塊中顏色為\(j\)的球有多少個。

時間複雜度:\(O(nlogn)\)

空間複雜度:\(O(n)\)

程式碼如下:

#include <cstdio>
#include <algorithm>
using namespace std;
 
const int maxn=2e5+5,pps=1e9+7;
 
bool vis[maxn];
int n,x,y,tot,ans=1,top;
int fac[maxn],inv[maxn],sta[maxn],insta[maxn];
int sum[maxn],now[maxn],pre[maxn<<2],son[maxn<<2],color[maxn];
 
int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
 
struct Ball {
    int c,w,id;
}b[maxn];
 
int quick(int a,int b) {
    int sum=1;
    while(b) {
        if(b&1)sum=1ll*sum*a%pps;
        a=1ll*a*a%pps,b>>=1;
    }
    return sum;
}
 
bool cmp1(Ball a,Ball b) {
    if(a.c==b.c)return a.w<b.w;
    return a.c<b.c;
}
 
bool cmp2(Ball a,Ball b) {
    if(a.w==b.w)return a.c<b.c;
    return a.w<b.w;
}
 
void add(int a,int b) {
    pre[++tot]=now[a];
    now[a]=tot,son[tot]=b;
}
 
void work1() {
    int lst=0;
    for(int i=1;i<=n;i++) {
        if(b[i].c!=b[lst].c) {
            lst=i;continue;
        }
        if(b[i].w+b[lst].w<=x) {
            add(b[i].id,b[lst].id);
            add(b[lst].id,b[i].id);
        }
    }
}
 
void work2() {
    int fake;b[n+1].w=1e9;
    for(fake=2;fake<=n;fake++)
        if(b[fake].c!=b[1].c)break;
    for(int i=2;i<=n;i++)
        if(b[i].c!=b[1].c) {
            if(b[i].w+b[1].w>y)break;
            add(b[i].id,b[1].id);
            add(b[1].id,b[i].id);
        }
        else if(b[i].w+b[fake].w<=y) {
            add(b[i].id,b[fake].id);
            add(b[fake].id,b[i].id);
        }
}
 
void prepare() {
    fac[0]=inv[0]=1;
    for(int i=1;i<=n;i++)
        fac[i]=1ll*fac[i-1]*i%pps;
    inv[n]=quick(fac[n],pps-2);
    for(int i=n-1;i;i--)
        inv[i]=1ll*inv[i+1]*(i+1)%pps;
}
 
void dfs(int u) {
    vis[u]=1;
    int c=color[u];
    if(!insta[c]) {
        sta[++top]=c;
        insta[c]=top;
        sum[top]=1;
    }
    else sum[insta[c]]++;
    for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
        if(!vis[v])dfs(v);
}
 
void calc() {
    int cnt=0;
    while(top) {
        ans=1ll*ans*inv[sum[top]]%pps;
        cnt+=sum[top],insta[sta[top]]=0,sta[top]=0,sum[top]=0,top--;
    }
    ans=1ll*ans*fac[cnt]%pps;
}
 
int main() {
    n=read(),x=read(),y=read(),prepare();
    for(int i=1;i<=n;i++)
        color[i]=b[i].c=read(),b[i].w=read(),b[i].id=i;
    sort(b+1,b+n+1,cmp1);work1();
    sort(b+1,b+n+1,cmp2);work2();
    for(int i=1;i<=n;i++)
        if(!vis[i])dfs(i),calc();
    printf("%d\n",ans);
    return 0;
}