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

猜你喜欢

转载自www.cnblogs.com/AKMer/p/10036776.html