• Codeforces Round #714 (Div. 2)


    Codeforces Round #714 (Div. 2)

    传送门

    B. AND Sequences

    题意:给一个数组,求有多少中排列,使得所有i满足

    a1 & a2 & … & ai = ai+1 & ai+2 & … & an

    题解:

    考虑 i 等于1时,若a[i]不为0,则ai+1 & ai+2 & … & an等于ai,可知后面所有数&a[1]都等于a[1],i等于n时依然。推出结论,两端的数必须&上所有数都等于它本身。

    之后方案数,只需从合法的数中取出两个放在两头,剩余数全排列就是答案。

    #include<iostream>
    using namespace std;
    #define ll long long
    const ll N=2e5+7;
    ll t,n,a[N];
    const ll mod=1e9+7;
    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;
    	}
    }
    int main(){
    	solve();
    	scanf("%lld",&t);
    	while(t--){
    		scanf("%lld",&n);
    		for(int i=1;i<=n;i++){
    			scanf("%lld",&a[i]);
    		}
    		ll x;
    		for(int i=1;i<=n;i++){
    			if(i==1)x=a[i];
    			else x&=a[i];
    		}
    		for(int i=1;i<=n;i++){
    			a[i]-=x;
    		}
    		ll cnt=0,sum;
    		for(int i=1;i<=n;i++){
    			if(a[i]==0){
    				cnt++;
    			}
    		}
    		printf("%lld
    ",cnt*(cnt-1)%mod*fac[n-2]%mod);
    	}
    }
    
    
    C. Add One

    题意:给一个数n操作m次,每次操作使所有数位加1,求最终数位个数。

    题解:

    首先一个数,可以把他数位都拆开,分别计算每位的贡献。

    所有数便小于10了,对于一个小于10的数x,它需要10-x次操作就可以进位,而进位的结果是多产生一位,两位的数分别是1,0。很明显的一个递归过程,用记忆化搜索优化时间即可。

    #include<iostream>
    #include<cstring>
    using namespace std;
    #define ll long long
    const ll N=2e5+7;
    const ll mod=1e9+7;
    ll t,n,m;
    ll dp[10][N];
    ll dfs(ll p,ll k){
    	ll co=10-p;
    	ll sum=0;
    	if(dp[p][k]!=-1){
    		return dp[p][k];
    	}
    	if(k>=co){
    		sum=(dfs(0,k-co)+dfs(1,k-co))%mod;
    	}
    	else{
    		sum=1;
    	}
    	return dp[p][k]=sum;
    }
    int main(){
    	memset(dp,-1,sizeof(dp));
    	scanf("%lld",&t);
    	while(t--){
    		scanf("%lld%lld",&n,&m);
    		ll ans=0;
    		while(n){
    			ll x=n%10;
    			ans+=dfs(x,m);
    			ans%=mod;
    			n/=10;
    		}
    		printf("%lld
    ",ans);
    	}
    }
    
    
    D. GCD and MST

    题意:

    给一个数组,若一个区间中(l,r)满足

    gcd(a[l],a[l+1]...a[r])== min(a[l],a[l+1]...a[r]),l到r连有一条区间最小值的边。

    相邻两点也有一条值为C的边。

    求最小生成树。

    题解:

    回忆ksk算法,从小到大枚举所有边,用并查集求解答案。

    所以我们可以从小到大枚举点,若那个点p与相邻连续的一段区间满足公式,则可以与区间中的所有连c[p]的边。

    这样最坏情况是n * n的,超时。

    我们可以想到若第一个点把中间一段区域连通时,第二个点在右段,往中间延伸,当它们相交时考虑一下有没有必要在继续延伸。

    对于中间一段,很明显已经连通的情况下两区域已在一个并查集中,无需再连边。

    对于左边一段,必定不满足公式,因为在枚举上一个点时,并没有延伸过去,说明区间gcd不等于区间最小值,那么右边的点在跨越中间后必然也包含上一个最小值,所以无法连接到左端。

    在这个结论下,很明显每个点最多连一次,时间为O(n)。

    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const ll N=2e5+7;
    ll t,n,C,a[N],fa[N],ans,cnt;
    pair<ll,ll>ma[N];
    ll fin(ll p){
    	if(fa[p]==p)return p;
    	else{
    		return fa[p]=fin(fa[p]);
    	}
    }
    ll gcd(ll a,ll b){
    	if(b==0)return a;
    	else{
    		return gcd(b,a%b);
    	}
    }
    void gao(ll p,ll c,ll z){
    	ll now=p+z;
    	while(1<=now&&now<=n){
    		ll gc=gcd(a[now],c);
    		if(gc==c){
    			ll f1=fin(now);
    			ll f2=fin(p);
    			if(f1!=f2){
    				fa[f1]=f2;
    				cnt--;
    				ans+=c;
    				now+=z;
    			}
    			else{
    				break;
    			}
    		}
    		else{
    			break;
    		}
    	}
    }
    int main(){
    	scanf("%lld",&t);
    	while(t--){
    		scanf("%lld%lld",&n,&C);
    		for(int i=1;i<=n;i++){
    			fa[i]=i;
    		}
    		for(int i=1;i<=n;i++){
    			scanf("%lld",&ma[i].first);
    			a[i]=ma[i].first;
    			ma[i].second=i;
    		}
    		sort(ma+1,ma+1+n);
    		ans=0,cnt=n;
    		for(int i=1;i<=n;i++){
    			ll p=ma[i].second;
    			ll c=ma[i].first;
    			if(c>=C){
    				break;
    			}
    			else{
    				gao(p,c,-1);
    				gao(p,c,1);
    			}
    		}
    		ans+=(cnt-1)*C;
    		cout<<ans<<"
    ";
    	}
    }#include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const ll N=2e5+7;
    ll t,n,C,a[N],fa[N],ans,cnt;
    pair<ll,ll>ma[N];
    ll fin(ll p){
    	if(fa[p]==p)return p;
    	else{
    		return fa[p]=fin(fa[p]);
    	}
    }
    ll gcd(ll a,ll b){
    	if(b==0)return a;
    	else{
    		return gcd(b,a%b);
    	}
    }
    void gao(ll p,ll c,ll z){
    	ll now=p+z;
    	while(1<=now&&now<=n){
    		ll gc=gcd(a[now],c);
    		if(gc==c){
    			ll f1=fin(now);
    			ll f2=fin(p);
    			if(f1!=f2){
    				fa[f1]=f2;
    				cnt--;
    				ans+=c;
    				now+=z;
    			}
    			else{
    				break;
    			}
    		}
    		else{
    			break;
    		}
    	}
    }
    int main(){
    	scanf("%lld",&t);
    	while(t--){
    		scanf("%lld%lld",&n,&C);
    		for(int i=1;i<=n;i++){
    			fa[i]=i;
    		}
    		for(int i=1;i<=n;i++){
    			scanf("%lld",&ma[i].first);
    			a[i]=ma[i].first;
    			ma[i].second=i;
    		}
    		sort(ma+1,ma+1+n);
    		ans=0,cnt=n;
    		for(int i=1;i<=n;i++){
    			ll p=ma[i].second;
    			ll c=ma[i].first;
    			if(c>=C){
    				break;
    			}
    			else{
    				gao(p,c,-1);
    				gao(p,c,1);
    			}
    		}
    		ans+=(cnt-1)*C;
    		cout<<ans<<"
    ";
    	}
    }
    
  • 相关阅读:
    第一个只出现一次的字符
    把数组排成最小的数
    整数中1出现的次数
    连续子数组的最大和
    最小的K个数
    数组中出现次数超过一半的数字
    字符串的排列
    二叉搜索树与双向链表
    numpy中ravel()和flatten()
    复杂链表的复制
  • 原文地址:https://www.cnblogs.com/whitelily/p/14646939.html
Copyright © 2020-2023  润新知