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<<"
";
	}
}
原文地址:https://www.cnblogs.com/whitelily/p/14646939.html