• AtCoder Grand Contest 012 D:Colorful Balls


    题目传送门:https://agc012.contest.atcoder.jp/tasks/agc012_d

    题目翻译

    给你一排一共(N)个球,每个球有一个颜色(c_i)和一个重量(w_i),如果两个球颜色相同,重量相加不超过(x)那我就可以交换这俩个球的位置。如果两个球颜色不同,重量相加不超过(y)那我也可以交换这俩球的位置。你可以随心所欲的交换或者不交换,求最后颜色序列的种数。(Nleqslant 2×10^5;c_ileqslant N;w_i,x,yleqslant10^9)

    题解

    首先,如果(a,b)可以交换,(b,c)可以交换,那么(a,c)也可以直接交换。

    对于颜色相同,我们把每个可以和当前颜色重量最小的球交换位置的球和当前颜色重量最小的球连一条边。那么对于可以以最小值为媒介交换的球也在一个联通块内了。

    对于颜色不同,假设当前球和重量最小的球也颜色不同,那就把当前球和重量最小的那个球连一条边,否则就把当前球与跟重量最小的球颜色不同的球当中重量最小的球连边。如果不存在这个球,那就不连。

    然后答案就是:

    [sumlimits_{i=1}^{m}frac{cnt_i!}{prodlimits_{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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    上传图片到FTP的实例
    关于浏览器兼容问题:获取div的值
    多张表数据导入到execl中
    把泛型转换成dataTable
    关于游标
    Excel 公式 两个时间比大小
    SQL 执行顺序
    VBA实现随意输入组合码,查询唯一标识码
    IIS7 503错误 Service Unavailable
    汇总文件数据 VBA
  • 原文地址:https://www.cnblogs.com/AKMer/p/10036776.html
Copyright © 2020-2023  润新知