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\n",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\n",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<<"\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<<"\n";
}
}