zoukankan      html  css  js  c++  java
  • AtCoder Regular Contest 116(C~E)

    AtCoder Regular Contest 116(C~E)

    C - Multiple Sequences

    题意:给一个n,m要求构造一个长为n的数组,使得每个后一位是前一位的倍数,并且所有值小于等于m,求方案数。

    题解:

    如何不重不漏的计算所有方案,我考虑枚举最后一位的值是多少,并求和。

    那么问题就变成的已知最后一位,求有多少种方案使数组的前一位是后一位的倍数。

    我们设dp[i] [j]表示长度为i时,第i位为j时的方案数。

    易知dp[i] [j]可以由所有dp[i-1] [x] (x为j的因子)转移,那么时间复杂度就为n*m。

    直接超时了。

    考虑如何优化,很明显一个数被质因数分解后最多的大概在20个因子左右,那么一个数组要求每一位是前一位的倍数,其实最多就增长了20多次。

    想当于,把因子视为板插入大小为n的数组里,每个板之间数都是一样大的,在

    板交界处翻倍,经典隔板法求方案。

    所以我们把dp[i] [j]表示长度为i,第i位j时的方案数(与上不同的是,这里前一位必须比j小,那么第一维就只要开20了)。

    #include<iostream>
    using namespace std;
    #define ll long long
    const ll mod=998244353;
    const ll N=2e5+7;
    ll n,m;
    ll f[27][N];
    ll fac[N];
    ll qpow(ll x,ll n) {
    	ll res=1;
    	for (;n;n>>=1,x=x*x%mod){
    		if (n&1)res=res*x%mod;
    	}
    	return res;
    }
    ll inv(ll a){
    	return qpow(a,mod-2)%mod;
    }
    void solve(){
    	fac[0]=1;
    	for(int i=1;i<N;i++) {
    		fac[i]=(fac[i-1]*i)%mod;
    	}
    }
    ll comb(ll n,ll k){
    	if(k>n)return 0;
    	if(k==1)return n;
    	return (fac[n]*inv(fac[k])%mod*inv(fac[n-k])%mod);
    }
    int main(){
    	solve();
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<=m;i++){
    		f[1][i]=1;
    	}
    	for(int i=1;i<=20;i++){
    		for(int j=1;j<=m;j++){
    			for(int k=2;k*j<=m;k++){
    				f[i+1][k*j]+=f[i][j];
    				f[i+1][k*j]%=mod;
    			}
    		}
    	}
    	ll ans=0;
    	for(int i=1;i<=20;i++){
    		for(int j=1;j<=m;j++){
    			ans=(ans+f[i][j]*comb(n-1,i-1)%mod)%mod;
    		}
    	}
    	cout<<ans<<"
    ";
    }
    

    D - I Wanna Win The Game

    题意:给一个n,m要求构造一个长为n的数组,使得所有数之和为m,且所有数的异或和为0求方案数。

    题解:

    首先思考怎么时异或和为0,显然将数组中所有数按二进制拆开,如果所有二进制数的个数之和为偶数的话,那么数组的异或和就为0。

    所以我们其实是要将m个小球放入n个盒子里,同时放的时候要按二进制放并且只能放偶数个(就先放偶数个1,再放偶数个2,再放偶数个4)有点类似于硬币问题求方案数同时在加个组合数学。

    #include<iostream>
    using namespace std;
    #define ll long long
    const ll N=5007;
    const ll mod=998244353;
    ll fac[N];
    ll pow(ll x,ll n,ll mod){
        ll res=1;
    	while(n>0){
    	   if(n%2==1){
    	   	 res=res*x%mod;
    	   }
    	   x=x*x%mod;
    	   n>>=1;
    	}
    	return res;
    }
    ll inv(ll a){
    	return pow(a,mod-2,mod);
    }
    void solve(){
    	fac[0]=1;
    	for(int i=1;i<N;i++) {
    		fac[i]=(fac[i-1]*i)%mod;
    	}
    }
    ll comb(ll n,ll k){
    	if(k>n)return 0;
    	if(k==1)return n;
    	return (fac[n]*inv(fac[k])%mod*inv(fac[n-k])%mod);
    }
    ll er[20],c[5007][5007];
    ll n,m;
    ll f[5007][5007];
    void init(){
    	er[0]=1;
    	for(int i=1;i<=20;i++){
    		er[i]=er[i-1]*2;
    	}
    	for(int i=0;i<=n;i++){
    		c[n][i]=comb(n,i);
    	}
    }
    
    int main(){
    	solve();
    	scanf("%lld%lld",&n,&m);
    	init();
    	f[0][0]=1;
    	for(int i=1;i<=20;i++){
    		if(er[i-1]>m){
    			printf("%lld
    ",f[i-1][m]);
    			break;
    		}
    		for(int j=0;j<=n;j+=2){
    			ll lin=er[i-1]*j;
    			if(lin>m)break;
    			for(int k=lin;k<=m;k++){
    				f[i][k]+=f[i-1][k-lin]*c[n][j]%mod;
    				f[i][k]%=mod;
    			}
    		}
    	}
    	return 0;
    }
    

    E - Spread of Information

    题意:给一个树,要你最多放k个点,以这k个点作为源点,bfs能到的最远距离最短。

    题解:

    我们直接二分答案,那么题目就转变成了:

    1、最远距离为x,使用k个点是否够用。

    我们可以在进行转换,若我们能求出:

    2、使最远距离为x,最少可使用y个点。

    通过比较y和x就可以判断x是否合法。

    那么对于问题2,这是一个很经典的树形dp题,与 https://www.luogu.com.cn/problem/P2016 类似。只是上面题是距离为1的边,此题是距离为x的点。

    但可以启发我们做此题,具体操作就是用两个数组,一个表示其子树最远为被感染的点的距离,一个表示子树中最近的被覆盖点的距离,两值相加小于x时,说明当前点也被感染。当最远未被感染的点距离为x时,说明此时必须覆盖此点已便感染儿子节点。

    #include<iostream>
    #include<vector>
    using namespace std;
    #define ll long long
    const ll N=2e5+7;
    const ll inf=1e9;
    ll n,m;
    vector<ll>ho[N];
    ll f[N],d[N],X,cnt;
    void dfs(ll p,ll fa){
    	f[p]=inf;
    	d[p]=0;
    	for(int i=0;i<ho[p].size();i++){
    		ll to=ho[p][i];
    		if(to==fa)continue;
    		dfs(to,p);
    		f[p]=min(f[p],f[to]+1);
    		d[p]=max(d[p],d[to]+1);
    	}
    	if(f[p]+d[p]<=X){
    		d[p]=-inf;
    	}
    	else if(d[p]==X){
    		cnt++;
    		f[p]=0;
    		d[p]=-inf;
    	}
    }
    bool check(ll x){
    	X=x;
    	cnt=0;
    	dfs(1,0);
    	if(d[1]>=0){
    		cnt++;
    	}
    	return cnt<=m;
    }
    int main(){
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<n;i++){
    		ll u,v;
    		scanf("%lld%lld",&u,&v);
    		ho[u].push_back(v);
    		ho[v].push_back(u);
    	}
    	ll l=0,r=n,ans=n;
    	while(l<=r){
    		ll mid=(l+r)/2;
    		if(check(mid)){
    			ans=mid;
    			r=mid-1;
    		}
    		else{
    			l=mid+1;
    		}
    	}
    	cout<<ans<<"
    ";
    }
    
  • 相关阅读:
    windows常用快捷键
    【Linux】查看系统位数
    【Centos】yum 安装mariaDB
    【Centos7 GRUB】修改开机等待时间
    Shell脚本编写规范
    【Shell Basic】source . 与 bash sh 的区别
    linux防火墙之 ufw
    【HotSpot】jps命令行详解
    【Ubuntu 16】网络配置文件
    【刷题】BZOJ 2179 FFT快速傅立叶
  • 原文地址:https://www.cnblogs.com/whitelily/p/14661647.html
Copyright © 2011-2022 走看看