寒假训练营6

阿宁的倍数

思路

对于这一个数,把他可以在那里提供贡献(也就是他的因子的地方都加上),然后就可以使用二分直接计算贡献。
而不是考虑一个一个的暴力

代码

#include <bits/stdc++.h>
using namespace std;
const int M=2e5+5;

vector<int>v[M];
vector<int>fac[M];
int a[M*2];

void init(int n=2e5) {
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j+=i)
            fac[j].push_back(i);//预处理出我的因子
}

void add(int x,int pos) {
    a[pos]=x;
    for(auto i:fac[x])
        v[i].push_back(pos);
}

int main() {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    init();
    int n,q;cin>>n>>q;
    for(int i=1;i<=n;i++) {
        int x;cin>>x;
        add(x,i);
    }
    for(int i=1;i<=q;i++) {
        int op,x;
        cin>>op>>x;
        if(op==2)cout<<v[a[x]].size()-(lower_bound(v[a[x]].begin(),v[a[x]].end(),x)-v[a[x]].begin()+1)<<'\n';
        else add(x,++n);
    }
    return 0;
}
//只看前面,不看后面,因子数是有限的,所以这样子是完全可以的
//根据某个东西,将他们进行分类,然后实现O1的查找
//也就是直接插入进行查找

阿宁的生成树

思路

首先lcm>=gcd,所以,能用gcd,就不要使用lcm
然后把最初的集合看成只有1这个点,i>k+1的话,就可以都加进来,并且gcd == 1
然后找到一个大的质数p,那么只要小于p-k的数,都是可以直接gcd == 1
对于剩下的这些数据,能找gcd的话,也就是p,n之间了,可以直接暴力枚举,因为质数的分布是比较密集的

代码

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int M=2e5+5;

bool st[M];
int n,k;
vector<int>v;
ll ans;

int main() {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>k;
    bool flag=0;//没有找到这个质数
    for(int i=n;i>=k+2;i--) {
        ans++;
        if(!flag) {//为什么只有这些数才会被加进区间里面呀
            bool f=0;
            for(int j=2;j*j<=i;j++)
                if(i%j==0) {
                    f=1;
                    break;
                }
            if(!f)flag=1;
            else v.push_back(i);
        }//首先是找到那个大的质数
        int u=i-k-1;
        if(u>1&&u<=k+1) {
            if(flag)ans++;
            else {
                int mn=1e9;
                for(auto x:v)mn=min(mn,__gcd(u,x));
                ans+=mn;
            }
            st[u]=1;
        }
    }
    for(int i=2;i<=min(n,k+1);i++)
        if(!st[i])ans+=i;
    cout<<ans;
    return 0;
}
//暴力的部分只需要考虑那个质数也就可以了,因为剩下的是一定可以做gcd的,也就是权值为1
//质数分布是很密集的,所以暴力的部分是很小的

阿宁睡大觉

思路

因为m比较小,容易想到状压。
总体思路是容斥,但是具体的方案数还是需要进行分析的。
然后用排列组合进行计算就可以了

代码

//利用容斥进行计算就可以了
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int M=2e5+5,mod=1e9+7;
using pii=pair<int,int>;

int kpow(int a,int b) {
    int ans=1;
    while(b) {
        if(b&1)ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}

int fact[M],infact[M];
void init(int n=2e5+5) {
    fact[0]=infact[0]=1;
    for(int i=1;i<=n;i++) {
        fact[i]=fact[i-1]*i%mod;
        infact[i]=kpow(fact[i],mod-2);
    }
}

int C(int a,int b) {
    return fact[a]*infact[b]%mod*infact[a-b]%mod;
}

pii a[15];

signed main() {
    init();
    int n,m;
    cin>>n>>m;
    for(int i=0;i<m;i++)cin>>a[i].first>>a[i].second;
    int ans=kpow(2,n-1);
    for(int i=1;i<(1<<m);i++) {
        int f=1;
        vector<pii>v;
        v.push_back({1,1});
        for(int j=0;j<m;j++)
            if(i>>j&1)v.push_back(a[j]),f=-f;//这样子看一下子就可以了
        sort(v.begin(),v.end());
        int siz=v.size()-1;
        //我是最后一个可以去到的位置
        int tmp=kpow(2,n+1-(v[siz].first+v[siz].second));
        for(int j=1;j<=siz;j++) {
            if(v[j].second>=v[j-1].second) {
                int x=v[j].first-v[j-1].first;
                int y=v[j].second-v[j-1].second;
                tmp=tmp*C(x+y,x)%mod;
            }
            else tmp=0;
        }
        ans=((ans+tmp*f)%mod+mod)%mod;
    }
    cout<<ans;
    return 0;
}
//也就是容斥进行计算
//每一种情况的话,看这一个与上一个的关系就可以了
//具体情况的话,要把每一种都算出来才是可以的
posted @ 2023-02-12 20:05  basicecho  阅读(25)  评论(0)    收藏  举报