zoukankan      html  css  js  c++  java
  • SCOI2019 RGB 和 LOJ2462 完美的集合

    RGB

    在点数为(N)的树上,每个点有各自的颜色(红色、绿色或蓝色),每条边有各自的长度。

    你的任务是计算点集对((U,V))的数量,满足:

    1. 集合(U)内的点均为红色或绿色,集合(V)内的点均为绿色或蓝色;

    2. 集合(U)和集合(V)都是连通的(若集合内任意两点间的简单路径上的点都属于该集合,则称该集合是连通的);

    3. 存在一个既属于集合(U)又属于集合(V)的点(x),使得对于任意一个属于集合(U)或集合(V)的点(y),满足点(x)和点(y)的距离不超过(M)(两点之间的距离即为它们之间的简单路径上的边的长度之和)。

    答案对(10^9+7)取模。

    对于所有数据点,(N≤2000)

    题解

    https://blog.csdn.net/sslz_fsy/article/details/101315047

    考虑只有一个绿点的情况,就是一个裸的树形DP,强制选当前点

    [f_R(u)=prod (f_R(v)+1)\ f_B(u)=prod (f_B(v)+1)\ ans=f_R(u)f_B(u) ]

    我们可以枚举所有绿点算一遍答案,考虑有哪些情况会算重。

    如果绿点不相连,很明显不会重,会重的情况只有绿色点聚成一坨的时候。

    我们要让一坨的点的贡献只算一次,发现(点数-边数=1)

    于是可以枚举每个绿点,在减去每条边的贡献就可以让一个连通块只算一次了。

    时间复杂度(O(N^2))

    CO int N=2e3+10;
    int W;
    char col[N];
    struct edge {int u,v,w;} E[N];
    vector<edge> to[N];
    int fR[N],fB[N],ans;
    
    void dfs(int u,int fa,int dis){
    	if(dis>W) {fR[u]=fB[u]=0; return;}
    	if(col[u]!='R') fB[u]=1;
    	if(col[u]!='B') fR[u]=1;
    	for(int i=0;i<(int)to[u].size();++i){
    		int v=to[u][i].v;
    		if(v==fa) continue;
    		dfs(v,u,dis+to[u][i].w);
    		fB[u]=mul(fB[u],fB[v]+1);
    		fR[u]=mul(fR[u],fR[v]+1);
    	}
    }
    IN void insert(int u){
    	dfs(u,0,0);
    	ans=add(ans,mul(fR[u],fB[u]));
    }
    IN void erase(int u,int v,int w){
    	dfs(u,v,w),dfs(v,u,w);
    	ans=add(ans,mod-mul(mul(fR[u],fB[u]),mul(fR[v],fB[v])));
    }
    int main(){
    	freopen("RGB.in","r",stdin),freopen("RGB.out","w",stdout);
    	int n=read<int>();read(W);
    	scanf("%s",col+1);
    	for(int i=1;i<n;++i){
    		int u=read<int>(),v=read<int>(),w=read<int>();
    		E[i]=(edge){u,v,w};
    		to[u].push_back((edge){u,v,w}),to[v].push_back((edge){v,u,w});
    	}
    	for(int i=1;i<=n;++i)if(col[i]=='G') insert(i);
    	for(int i=1;i<n;++i)if(col[E[i].u]=='G' and col[E[i].v]=='G') erase(E[i].u,E[i].v,E[i].w);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    完美的集合

    小A有一棵(N)个点的带边权的树,树的每个节点有重量(w_i)和价值(v_i)

    现在小A要从中选出若干个节点形成一个集合(S),满足这些节点重量之和(leq M)并且构成一个连通块。小A是一个完美主义者,因此他只会选择节点价值之和最大的那些(S)。我们称这样的集合(S)为完美的集合。

    现在小(A)要从所有完美的集合中选出(K)个,并对这(K)个完美的集合分别进行测试。在这(K)次测试开始前,小A首先需要一个点(x)来放置他的测试装置,这个测试装置的最大功率为( ext{Max})

    接下来的每次测试,小A会对测试对象(S)中的所有点进行一次能量传输,对一个点(y)进行能量传输需要的功率为( ext{dist}(x,y) imes v_y),其中( ext{dist}(x,y))表示点(x,y)在树上的最短路长度。因此,如果(S)中存在一个点(y),满足( ext{dist}(x,y) imes v_y> ext{Max}),测试就会失败。同时,为了保证能量传输的稳定性,测试装置所在的点(x)需要在集合(S)中,否则测试也会失败。

    现在小A想知道,有多少种从所有完美的集合选出(K)个的方法,使得他能找到一个放置测试装置的点,来完成他的测试呢?

    你只需要输出方案数对(11920928955078125)取模的结果。

    对于(100\%)的数据,(Nleq 60,Mleq 10000,C_ileq 10000,K,w_i,v_ileq 10^9, ext{Max}leq 10^{18})

    题解

    http://jklover.hs-blog.cf/2020/04/10/Loj-2462-完美的集合/

    此题可以看成一道二合一:树上连通块的DP技巧 + 奇怪的组合数取模。

    // Output
    CO int mod=11920928955078125;
    IN int add(int a,int b){
    	return (a+=b)>=mod?a-mod:a;
    }
    IN int mul(int a,int b){
    	int64 ans=a*b-(int)((float128)a/mod*b+1e-8)*mod;
    	return ans<0?ans+mod:ans%mod;
    }
    IN int fpow(int a,int b){
    	int ans=1;
    	for(;b;b>>=1,a=mul(a,a))
    		if(b&1) ans=mul(ans,a);
    	return ans;
    }
    IN int inv(int a){
    	return fpow(a,mod/5*4-1); // phi-1
    }
    
    typedef array<int,23> poly;
    
    poly operator*(CO poly&a,CO poly&b){
    	poly c={};
    	for(int j=0;j<23;++j)if(b[j]) // more 0s in b
    		for(int i=0;i+j<23;++i)if(a[i])
    			c[i+j]=add(c[i+j],mul(a[i],b[j]));
    	return c;
    }
    pair<int,int> operator+(CO pair<int,int>&a,CO pair<int,int>&b){
    	return {mul(a.first,b.first),a.second+b.second};
    }
    pair<int,int> operator-(CO pair<int,int>&a,CO pair<int,int>&b){
    	return {mul(a.first,inv(b.first)),a.second-b.second};
    }
    
    poly P[10000];
    int C[23][23];
    
    void init(){
    	C[0][0]=1;
    	for(int i=1;i<23;++i){
    		C[i][0]=C[i][i]=1;
    		for(int j=1;j<i;++j) C[i][j]=add(C[i-1][j],C[i-1][j-1]);
    	}
    	P[0]={1};
    	for(int i=1;i<10000;++i){
    		if(i%5!=0) P[i]=P[i-1]*(poly){i,1};
    		else P[i]=P[i-1];
    	}
    }
    poly trans(CO poly&a,int k){
    	static int power[23];
    	power[0]=1;
    	for(int i=1;i<23;++i) power[i]=mul(power[i-1],k);
    	poly b={};
    	for(int i=0;i<23;++i)for(int j=0;j<=i;++j)
    		b[j]=add(b[j],mul(a[i],mul(power[i-j],C[i][j])));
    	return b;
    }
    poly fac_poly(int n){
    	if(n<10000) return P[n];
    	int k=n/10*10;
    	poly ans=fac_poly(k/2);
    	ans=ans*trans(ans,k/2);
    	for(int i=k+1;i<=n;++i)if(i%5!=0)
    		ans=ans*(poly){i%mod,1};
    	return ans;
    }
    pair<int,int> fac_pair(int n){
    	pair<int,int> ans={fac_poly(n)[0],n/5};
    	if(n>=5) ans=ans+fac_pair(n/5);
    	return ans;
    }
    int binom(int n,int k){
    	if(n<k) return 0;
    	pair<int,int> ans=fac_pair(n)-fac_pair(k)-fac_pair(n-k);
    	if(ans.second>=23) return 0;
    	return mul(ans.first,fpow(5,ans.second));
    }
    
    // Tree DP
    CO int N=70,M=1e4+10,inf=1e9;
    int n,m,K,Max;
    int w[N],val[N],dist[N][N];
    vector<int> to[N];
    
    int valid[N],rnk[N],siz[N],fa[N],_fa[N],idx;
    
    void dfs(int u){
    	siz[u]=1,rnk[++idx]=u;
    	for(int v:to[u])if(v!=fa[u] and valid[v])
    		fa[v]=u,dfs(v),siz[u]+=siz[v];
    }
    
    int f[N][M],g[N][M],mx;
    
    pair<int,int> calc(int x,int y){
    	idx=0,fa[x]=0,dfs(x);
    	for(int i=0;i<=m;++i) f[idx+1][i]=0,g[idx+1][i]=1;
    	for(int i=idx;i>=1;--i){
    		int u=rnk[i];
    		for(int j=0;j<=m;++j){
    			int v1=j>=w[u]?f[i+1][j-w[u]]+val[u]:0;
    			int v2=f[i+siz[u]][j];
    			if(u==y and j<w[u]) // can't choose y
    				f[i][j]=g[i][j]=0;
    			else if(u==y or (j>=w[u] and v1>v2))
    				f[i][j]=v1,g[i][j]=g[i+1][j-w[u]];
    			else if(j<w[u] or v1<v2)
    				f[i][j]=v2,g[i][j]=g[i+siz[u]][j];
    			else f[i][j]=v1,g[i][j]=g[i+1][j-w[u]]+g[i+siz[u]][j];
    		}
    	}
    	return {f[1][m],g[1][m]};
    }
    int solve(int x,int y){
    	for(int i=1;i<=n;++i)
    		valid[i]=dist[x][i]*val[i]<=Max and (!y or dist[y][i]*val[i]<=Max);
    	if(!valid[x] or (y and !valid[y])) return 0;
    	pair<int,int> ans=calc(x,y);
    	if(ans.first<mx) return 0;
    	return binom(ans.second,K);
    }
    
    signed main(){
    	init();
    	read(n),read(m),read(K),read(Max);
    	for(int i=1;i<=n;++i) read(w[i]);
    	for(int i=1;i<=n;++i) read(val[i]);
    	for(int i=1;i<=n;++i)for(int j=1;j<=n;++j) dist[i][j]=i==j?0:inf;
    	for(int i=1;i<n;++i){
    		int u=read<int>(),v=read<int>(),l=read<int>();
    		to[u].push_back(v),to[v].push_back(u);
    		dist[u][v]=dist[v][u]=l;
    	}
    	for(int k=1;k<=n;++k)
    		for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)
    			dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
    	fill(valid+1,valid+n+1,1);
    	for(int i=1;i<=n;++i) mx=max(mx,calc(i,0).first);
    	copy(fa+1,fa+n+1,_fa+1); // fa will change when solve calls calc
    	int ans=0;
    	for(int i=1;i<=n;++i)
    		ans=add(ans,add(solve(i,0),mod-(_fa[i]?solve(i,_fa[i]):0)));
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    ASP.NET的票据工具类FormsAuthenticationTicket
    FormsAuthenticationTicket学习笔记
    FormsAuthentication与Session超时时间不一的解决方法
    asp.net mvc 从数据库中读取图片的实现代码
    ASP.NET下载远程图片保存到本地的方法、保存抓取远程图片
    获取客户端的IP地址
    C#实现DNS解析服务
    Linux下安装Nginx详细图解教程
    手动配置Ubuntu Linux系列3-缺省网关和主机名
    web运维第一篇:nginx配置文件详解笔记
  • 原文地址:https://www.cnblogs.com/autoint/p/12685723.html
Copyright © 2011-2022 走看看