CF2020(Codeforces Round 976 (Div. 2))

CF2020

A. Find Minimum Operations

难度:红
转换进制,每一位上数字相加。


#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll t,n,k,ans;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n>>k;
        if(k==1)cout<<n<<'\n';
        else{
            ans=0;
            while(n>=k){
                ans+=n%k;
                n/=k;
            }
            ans+=n;
            cout<<ans<<'\n';
        }
    }
    return 0;
}

B. Brightness Begins

难度:橙
场上临时打了个表找规律。发现 \(n-\lfloor\sqrt{n}\rfloor=k\)。于是过了。


#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll t,k;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        cin>>k;
        if(k==0){
            cout<<1<<'\n';
            continue;
        }
        ll a=sqrtl(k);
        if((a+k)-(ll)sqrtl(a+k)==k)cout<<a+k<<'\n';
        else cout<<a+k+1<<'\n';
    }
    return 0;
}

C. Bitwise Balancing

难度:橙-黄
对于每一位分类讨论。
场上分讨调了好久没出来,悲剧了qwq


#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll t,ans,b,c,d;
ll fj(ll b,ll c,ll d){
    ll ret=0;
    for(ll i=0;i<=62;i++){
        ll nowd=((d>>i)&(1ll)),nowc=((c>>i)&(1ll)),nowb=((b>>i)&(1ll));
        if((nowd==0)&&(nowc==0)&&(nowb==0))ret+=0;
        else if((nowd==0)&&(nowc==0)&&(nowb==1))return -1;
        else if((nowd==0)&&(nowc==1)&&(nowb==0))ret+=(1ll<<i);
        else if((nowd==0)&&(nowc==1)&&(nowb==1))ret+=(1ll<<i);
        else if((nowd==1)&&(nowc==0)&&(nowb==0))ret+=(1ll<<i);
        else if((nowd==1)&&(nowc==0)&&(nowb==1))ret+=0;
        else if((nowd==1)&&(nowc==1)&&(nowb==0))return -1;
        else ret+=0;//if((nowd==1)&&(nowc==1)&&(nowb==1))
    }
    if(ret>(1ll<<61))return -1;
    return ret;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        cin>>b>>c>>d;
        cout<<fj(b,c,d)<<'\n';
    }
    return 0;
}

D. Connect the Dots

难度:黄-绿
首先,直接用并查集维护会T飞。
先把操作记录下来,再遍历 \(1\sim n\) 传递操作,并查集维护一下就行了。


#include<bits/stdc++.h>
#define ll long long
#define mxn 200010
using namespace std;
ll T,n,m,a,d,k,f[mxn],num[mxn][15];
int fnd(int x){
    if(x==f[x])return x;
    return f[x]=fnd(f[x]);
}
void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=10;j++)
            num[i][j]=0;
        f[i]=i;
    }
    for(int i=1;i<=m;i++){
        cin>>a>>d>>k;
        num[a][d]=max(num[a][d],k);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=10;j++){
            if(i<=j)break;
            if(num[i-j][j])f[fnd(i)]=fnd(i-j);
        }
        for(int j=1;j<=10;j++)
            if(num[i][j]>1&&i+j<=n)
                num[i+j][j]=max(num[i+j][j],num[i][j]-1);
    }
    set<int> s;
    for(int i=1;i<=n;i++)
        s.emplace(fnd(i));
    cout<<s.size()<<'\n';
    return;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>T;
    while(T--)solve();
}

E. Expected Power

难度:绿
看见期望我就跑
考虑概率dp。
容易发现不管怎么异或,最终异或和的范围总会在 \([0,1023]\) 内。
定义 \(dp_{i,j}\) 为前 \(i\) 位中异或和为 \(j\) 的概率。
当不选 \(a_i\) 时,有
\(dp_{i,j}=dp_{i,j}+(1-p_i)*dp_{i-1,j}\)
选了 \(a_i\) 时,有
$ dp_{i,j}=dp_{i,j}+p_i*dp_{i-1,j\oplus a_i} $
注意到 \(dp_{i}\) 只由 \(dp_{i-1}\) 转移,所以可以省一维。


#include<bits/stdc++.h>
#define ll long long 
#define mod 1000000007
#define mxn 200010
using namespace std;
ll t,n,p[mxn],a[mxn],frac,ans;
ll quickpow(ll a,ll x){
    ll ret=1,base=a;
    while(x){
        if(x&1)ret=ret*base%mod;
        base=base*base%mod;
        x>>=1;
    }
    return ret;
}
void solve(){
    cin>>n;
    ans=0;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)cin>>p[i];
    ll dp[1030][2]={0};
    dp[0][0]=1;
    for(int i=1;i<=n;i++){
        ll now=i%2,lst=(i%2)^1;
        for(int j=0;j<1024;j++){
            dp[j][now]=0;
            dp[j][now]=(dp[j][now]+dp[j][lst]*(10000-p[i]))%mod;
            dp[j][now]=(dp[j][now]+dp[j^a[i]][lst]*p[i])%mod;
            dp[j][now]=dp[j][now]*frac%mod;          
        }
    }
    for(ll i=0;i<1024;i++)
        ans=(ans+i*i*dp[i][n%2]%mod)%mod;          
    cout<<ans<<'\n';
    return;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    frac=quickpow(10000,mod-2);
    cin>>t;
    while(t--)solve();
    return 0;
}

注:该代码不保证可过,我交的时候TLE了,后面加了快读快写才过的。

F. Count Leaves

难度:紫
这题实在想不到正解,被迫看solution了qaq

容易发现,叶子数量等于选择 \(d+1\) 个数 \(((a_0,a_1,...,a_d),\) 其中 \(a_d=n,a_i|a_{i+1}(i=0...d-1))\) 的方法数。
定义 \(g(n)=f(n,d),(d\) 已给定)。手推一下发现这是个积性函数,即当 \(p,q\) 互质时,有 \(g(p)*g(q)=g(p*q)\)
\(n\) 为某个素数 \(p\) 的非负整数幂次时(即 \(n=p^b\)),明显 \(a_0,a_1,...,a_d\) 可以写成 \(p_{b_0},p_{b_1},...,p_{b_d}=x(b_0\leq b_1\leq...\leq b_d=b)\)
于是有 \(g(n)=\tbinom{x+d}{d}\)

然后官方solution上说是受到 Meissel–Lehmer 算法(一种快速计算质数个数的算法)的启发搞了个dp。
当然关于这个算法的介绍也不用去看,OI-wiki上写的像一坨。

总之是搞了一个dp。令 \(dp(n,x)=\sum^n_{i=1,spf(i)\geq x}g(i)\),其中 \(spf(i)\) 表示 \(i\) 的最小质因子。
我猜看到这么大一串式子你会懵逼。讲的就是从 \(1..n\) 中选出满足其最小质因子大于等于 \(x\) 的数 \(i\),求 \(g(i)\) 的和。
于是这里有一大堆式子:

\[dp(n,p)=\left\{ \begin{array}{lcl} 0&&n=0\\ 1&&p>n\\ dp(n,p+1)&&p\ \rm{is\ not\ a\ prime}\\ \displaystyle\sum_{i=0\ to\ p^i\leq n}dp(\lfloor\frac{x}{p}\rfloor,p+1)f(p^{ik},d)&&\rm otherwise \end{array} \right. \]

手推过程略。
明显,\(g(n)=dp(n,2)\),于是可以求出答案。
时间复杂度 \(O(n^{\frac{2}{3}})\)

这里就先把官方代码贴上面吧(可读性极高),到时候再补自己的。


#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define big_lim 1001
#define small_lim 1000001
#define mod 1000000007
#define ncrlim 3500000
using namespace std;
ll powmod(ll base,ll exponent){
	ll ans=1;
	if(base<0)base+=mod;
	while(exponent){
		if(exponent&1)ans=(ans*base)%mod;
		base=(base*base)%mod;
		exponent/=2;
	}
	return ans;
}
ll gcd(ll a,ll b){
	if(b==0) return a;
	else return gcd(b,a%b);
}
ll primes_till_i[small_lim];
ll primes_till_bigger_i[big_lim];
vector<ll> sieved_primes[small_lim];
vector<ll> sieved_primes_big[big_lim];
vector<int> prime;
int N,k,d;
void sieve(){//普通的埃氏筛
	vector<int> lpf(small_lim);
	ll pw;
	for(int i=2;i<small_lim;i++){
		if(!lpf[i]){
			prime.push_back(i);
			lpf[i]=i;
		}
		for(int j:prime){
			if((j>lpf[i])||(j*i>=small_lim))break;
			lpf[j*i]=j;
		}
	}
	for(int i=2;i<small_lim;i++)
		primes_till_i[i]=primes_till_i[i-1]+(lpf[i]==i);
}
ll count_primes(ll n,int ind){
	if(ind<0)return n-1;
	if(1ll*prime[ind]*prime[ind]>n){
		if(n<small_lim)return primes_till_i[n];
		if(primes_till_bigger_i[N/n])return primes_till_bigger_i[N/n];
		int l=-1,r=ind;
		while(r-l>1){
			int mid=(l+r)>>1;
			if(1ll*prime[mid]*prime[mid]>n)r=mid;
			else l=mid;
		}
		return primes_till_bigger_i[N/n]=count_primes(n,l);
	}
	int sz;
	if(n<small_lim)sz=sieved_primes[n].size();
	else sz=sieved_primes_big[N/n].size();
	ll ans;
	if(sz<=ind){
		ans=count_primes(n,ind-1);
		ans-=count_primes(n/prime[ind],ind-1);
		ans+=ind;
		if(n<small_lim) sieved_primes[n].push_back(ans);
		else sieved_primes_big[N/n].push_back(ans);
	}
	if(n<small_lim) return sieved_primes[n][ind];
	else return sieved_primes_big[N/n][ind];
}
ll count_primes(ll n){
	if(n<small_lim) return primes_till_i[n];
	if(primes_till_bigger_i[N/n]) return primes_till_bigger_i[N/n];
	return count_primes(n,prime.size()-1);
}
int fact[ncrlim];
int invfact[ncrlim];
void init_fact(){
	fact[0]=1;
	for(int i=1;i<ncrlim;i++) fact[i]=(1ll*fact[i-1]*i)%mod;
	invfact[ncrlim-1]=powmod(fact[ncrlim-1], mod-2);
	for(int i=ncrlim-1;i>0;i--) invfact[i-1]=(1ll*invfact[i]*i)%mod;
}
int ncr(int n,int r){
	if(r>n||r<0)return 0;
	int ans=fact[n];
	ans=(1ll*ans*invfact[n-r])%mod;
	ans=(1ll*ans*invfact[r])%mod;
	return ans;
}
ll calculate_dp(ll n,int ind){
	if(n==0)return 0;
	if(prime[ind]>n) return 1;
	ll ans=1,temp;
	if(1ll*prime[ind]*prime[ind]>n){
		temp=ncr(k+d,d);
		temp*=count_primes(n)-ind;
		ans=(ans+temp)%mod;
		return ans;
	}
	ans=0;
	ll gg=1;
	ll mult=d;
	while(gg<=n){
		temp=calculate_dp(n/gg,ind+1);
		temp*=ncr(mult,d);
		ans=(ans+temp)%mod;
		mult+=k;
		gg*=prime[ind];
	}
	return ans;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
	sieve();//质数预处理
	init_fact();//组合数预处理
	int t;
	cin>>t;
	while(t--){
        cin>>N>>k>>d;
        cout<<calculate_dp(N,0)<<'\n';
		for(int i=1;i<big_lim;i++){
			primes_till_bigger_i[i]=0;
			sieved_primes_big[i].clear();
		}
	}
	return 0;
}
posted @ 2024-10-02 12:22  nagato__yuki  阅读(59)  评论(0)    收藏  举报