容斥专题总结

容斥专题总结

A:How many integers can you find

过于水,但是有细节

\[\ \]

\[\ \]

B: Eddy's爱好

枚举k的值,直接开k次方根,为了防止爆精度,我手写了一下

然后就是对于因数容斥,或者是直接套莫比乌斯系数


const int N=4e5+10;
const ll INF=(1ll<<62)-1;

ll n;

ll qsm(ll x,ll k) {
    ll res=1;
    while(k) {
        if(k&1) {
            double t=(double)res*x;
            if(t>INF) {
                res=INF;
                break;
            }
            else res=res*x;
        }
        double t=(double)x*x;
        if(t>INF) x=INF;
        else x=x*x;
        k>>=1;
    }
    return res;
}


ll yroot(ll x,ll y){
    ll l=1,r=2e9,res=1;
    while(l<=r) {
        ll mid=(l+r)>>1;
        if(qsm(mid,y)<=x) res=mid,l=mid+1;
        else r=mid-1;
    }
    return res;
}

ll dp[100];



int main() {
    while(~scanf("%lld",&n)) {
        ll res=0;
        rep(i,2,60) dp[i]=yroot(n,i)-1;
        drep(i,60,2) for(int j=i+i;j<=60;j+=i) dp[i]-=dp[j];//k=i的情况会在j中被多次计算
        rep(i,2,60) res+=dp[i];
        printf("%lld\n",res+1);
    }
}

\[ \ \]

\[ \ \]

C: Coprime

提出n,m的质因数,然后二分答案,直接二进制枚举来容斥


const int N=4e5+10;
const ll INF=(1ll<<62)-1;

int k;

int fac[N],n;

void Div(int x) {
    for(int i=2;i*i<=x;++i) if(x%i==0) {
        fac[n++]=i;
        while(x%i==0) x/=i;
    }
    if(x>1) fac[n++]=x;
}


ll Check(ll mid){ 
    ll res=0;
    rep(S,0,(1<<n)-1) {
        ll t=1,cnt=0;
        rep(i,0,n-1) if(S&(1<<i)) cnt^=1,t=t*fac[i];
        if(cnt) res-=mid/t;
        else res+=mid/t;
    }
    return res;
}

int main(){
    rep(kase,1,rd()) {
        n=0;Div(rd()),Div(rd());
        k=rd();
        sort(fac,fac+n);
        n=unique(fac,fac+n)-fac;
        ll l=1,r=1e15,res=-1;
        while(l<=r) {
            ll mid=(1ll*l+1ll*r)>>1;
            if(Check(mid)>=k) res=mid,r=mid-1;
            else l=mid+1;
        }
        printf("Case %d: %lld\n",kase,res);
    }
}

\[ \ \]

\[ \ \]

D: GCD

首先外层的区间可以容斥

然后是对于每个\(1 \leq i \leq n\)二进制枚举容斥\(1..m\)中互质的个数

const int N=1e5+10;
const ll INF=(1ll<<62)-1;

int a,b,c,d,k;
vector <int> fac[N];


ll Solve(int n,int m,int k) {
    if(!k||!n||!m) return 0;
    n/=k,m/=k;
    if(n<m) swap(n,m);
    ll res=0;
    rep(i,1,n) {
        int t=min((int)i,m);
        int k=fac[i].size();
        rep(j,0,(1<<k)-1){
            ll x=1,cnt=0;
            rep(o,0,k-1) if(j&(1<<o)) x=x*fac[i][o],cnt^=1;
            if(cnt) res-=t/x;
            else res+=t/x;
        }
    }
    return res;
}



int main(){
    rep(i,2,N-1) if(!fac[i].size()) for(int j=i;j<N;j+=i) fac[j].push_back((int)i);
    rep(kase,1,rd()) {
        a=rd(),b=rd(),c=rd(),d=rd(),k=rd();
        ll res=Solve(b,d,k)-Solve(a-1,d,k)-Solve(b,c-1,k)+Solve(a-1,c-1,k);
        printf("Case %d: %lld\n",kase,res);
    }
}

\[\ \]

\[ \ \]

E: Co-prime

基本思想之前都出现过了,跳过吧

\[ \ \]

\[ \ \]

F: Frogs

这个题还是非常有点东西的对吧

首先你要知道这样一件事
对于任何的\(gcd(n,m)=1\),$\lbrace n*i  mod  m|i \in Z \rbrace \(=\){0,1,..,m-1}$

然后其实对于任意\(gcd(n,m)=g\),$\lbrace ni  mod  m|i \in Z \rbrace \(=\){0,g,g2,..,m-1}$

所以每个青蛙能走到的地方就确定了,设这些\(gcd\)的值为\(a[1..n]\)

这时候我们模拟一下二进制枚举的过程,即取出其中的一些数,求出他们的\(lcm(lowest \ common \ multiple)\),当数的个数为奇数时加,偶数时减

即当你每次多选择一个数时,系数的符号取相反数,而\(lcm\)的情况数并不多,也就是\(m\)的因子个数

所以直接dp转移求系数即可

const int N=1e4+10;

ll gcd(ll a,ll b) {return b==0?a:gcd(b,a%b); }

int n,m;
int a[N];
int fac[N],c;
ll dp[N];
int lcm[2000][2000];

int gcd(int a,int b){ return b==0?a:gcd(b,a%b); }

int main(){
    rep(kase,1,rd()) {
        n=rd(),m=rd();
        rep(i,1,n) a[i]=gcd(rd(),m);
        sort(a+1,a+n+1);
        n=unique(a+1,a+n+1)-a-1;
        c=0;
        rep(i,1,sqrt(m+0.5)) if(m%i==0) {
            fac[++c]=i;
            if(i*i!=m) fac[++c]=m/i;
        }
        sort(fac+1,fac+c+1);
        rep(i,1,c) dp[i]=0;
        rep(i,1,n) a[i]=lower_bound(fac+1,fac+c+1,a[i])-fac;
        rep(i,1,c) rep(j,1,c) lcm[i][j]=lower_bound(fac+1,fac+c+1,fac[i]/gcd(fac[i],fac[j])*fac[j])-fac;
        rep(i,1,n) {
            int x=a[i];
            drep(j,c,1) {
                dp[lcm[x][j]]-=dp[j];
            }
            dp[a[i]]++;
        }
        ll ans=0;
        rep(i,1,c) ans+=1ll*dp[i]*(m/fac[i])*(m-fac[i])/2;
        printf("Case #%d: %lld\n",kase,ans);
    }
}

\[ \ \]

\[ \ \]

\[ \ \]

\[ \ \]

G: The Monkey King

刚开始做的时候没想到可以\(n\ ln \ n\)写,头都想破了。。。

枚举大猴子拿了\(x\)个,然后容斥其他人拿的比他多的情况

统计时候,枚举有至少\(i\)个人比他多,答案是\(C(n+m-x*(i+1)-1,m-2)\)

其实是插板法求组合,然后就可以顺利的容斥了

int n,m;
ll po[N]={1},Inv[N]={1,1};

ll C(int n,int m){ 
    if(n<0||m<0||n<m) return 0;
    return po[n]*Inv[m]%P*Inv[n-m]%P;
}

ll Solve(int x) {
    ll ans=C(n+m-x-2,m-2);
    rep(i,1,min(m-1,n/x-1)) {
        if(i&1) (ans=ans-C(m-1,i)*C(n+m-x*(i+1)-2,m-2))%=P;
        else ans=(ans+C(m-1,i)*C(n+m-x*(i+1)-2,m-2))%P;
    }
    return ans=(ans%P+P)%P;
}


int main(){
    rep(i,1,N-1) po[i]=po[i-1]*i%P;
    rep(i,2,N-1) Inv[i]=(P-P/i)*Inv[P%i]%P;
    rep(i,1,N-1) Inv[i]=Inv[i]*Inv[i-1]%P;
    rep(kase,1,rd()) {
        n=rd(),m=rd();
        if(m==1) {
            puts("1");
            continue;
        }
        ll ans=0;
        rep(i,(n-1)/m+1,n) ans+=Solve(i);
        ans%=P;
        ans=(ans%P+P)%P;
        printf("%lld\n",ans);
    }
}

\[ \ \]

\[ \ \]

H: Y sequence

这个题是真的没有素质

首先它和B题比就多了个二分,但如果真的写二分就TLE了! 必须写迭代

其次是不能手写开次方根了,只能用pow函数,而且还会爆精度


const ll INF=(1ll<<62)-1;

ll k;
int r;

int w[100],notpri[100],pri[100],cp;
int num[100],maxpri[100],nc;


inline ll Solve(ll n) {
    ll res=0;
    rep(i,1,nc) if(maxpri[i]<=r) res+=1ll*w[i]*((ll)pow(n+0.1,1.0/num[i])-1);
    return n-res-1;
}




int main() {
    rep(i,2,65) {
        if(!notpri[i]) {
            pri[++cp]=i;
            for(int j=i+i;j<=65;j+=i) notpri[j]=1;
        }
    }
    rep(i,2,65) {
        int x=i,t=-1,ma=0;
        rep(j,1,cp) {
            int cnt=0;
            while(x%pri[j]==0) cnt++,x/=pri[j];
            if(cnt>1) {
                t=0;
                break;
            }
            if(cnt) t=-t,ma=max(ma,pri[j]);
        }
        if(t) w[++nc]=t,num[nc]=i,maxpri[nc]=ma;
    }
    rep(kase,1,rd()){
        scanf("%lld",&k);r=rd();
        ll ans=k;
        while(1) {
            ll tmp=Solve(ans);
            if(k==tmp) break;
            ans+=k-tmp;
        }
        printf("%lld\n",ans);
    }
}


\[ \ \]

\[ \ \]

I: MG loves string

目测这题做法也奇多无比,我用了矩阵乘法

首先26个字母组成多个环,环长的种类最多只有6种

所以我直接将这6种环长状压,矩阵快速幂即可



const int N=1e5+10,P=1e9+7;

int n;
int nxt[26],c[26];
char str[30];
int vis[27];
int A,k[N],id[N];
int cnt;

ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); }

struct Mat{
    int a[1<<6][1<<6];
    void init(){ rep(i,0,A) rep(j,0,A) a[i][j]=0; }
    void Get1(){ rep(i,0,A) a[i][i]=1; }
    Mat operator * (const Mat x) const{
        Mat res; res.init();
        rep(i,0,A) rep(j,0,A) rep(o,0,A) (res.a[i][o]+=1ll*a[i][j]*x.a[j][o]%P)%=P;
        return res;
    }
}res,x;
ll f[1][1<<6],ans[1][1<<6];




int main(){
    rep(kase,1,rd()) {
        n=rd();
        scanf("%s",str);
        rep(i,0,25) nxt[i]=str[i]-'a';
        cnt=0;
        memset(vis,0,sizeof vis);
        rep(i,0,25) {
            int p=i;c[i]=0;
            do {
                p=nxt[p];
                c[i]++;
            } while(p!=i);
            if(!vis[c[i]]) {
                vis[c[i]]=1;
                id[c[i]]=cnt;
                k[cnt++]=c[i];
            }
        }
        A=(1<<cnt)-1;
        x.init(),res.init();res.Get1();
        rep(i,0,A) {
            rep(j,0,25) {
                x.a[i][i|(1<<id[c[j]])]++;
            }
        }
        while(n) {
            if(n&1) res=res*x;
            x=x*x;
            n>>=1;
        }
        memset(f,0,sizeof f);memset(ans,0,sizeof ans);
        f[0][0]=1;
        rep(i,0,0) rep(j,0,A) rep(o,0,A) (ans[i][o]+=1ll*f[i][j]*res.a[j][o]%P)%=P;
        ll Ans=0;
        rep(S,0,A) {
            ll t=1;
            rep(i,0,cnt-1) if(S&(1<<i)) t=t*k[i]/gcd(t,k[i]);
            (Ans+=t*ans[0][S]%P)%=P;
        }
        printf("%lld\n",Ans);
    }
}

\[ \ \]

\[ \ \]

J: Harry And Magic Box

dp容斥就好了,\(dp[i][j]\)表示\(i\)\(j\)列的都放了的方案数,枚举小于\(i\)或小于\(j\)的方案减掉就好了

int n,m,q;
ll Inv[N*N]={1,1},po[N*N]={1};
ll B[N][N],p2[N*N]={1};

inline ll C(int n,int m){
    if(n<0||m<0||n<m) return 0;
    return po[n]*Inv[m]%P*Inv[n-m]%P;
}



int main(){
    rep(i,1,N*N-1) po[i]=po[i-1]*i%P,p2[i]=p2[i-1]*2%P;
    rep(i,2,N*N-1) Inv[i]=(P-P/i)*Inv[P%i]%P;
    rep(i,1,N*N-1) Inv[i]=Inv[i]*Inv[i-1]%P;
    rep(i,0,50) {
        rep(j,0,50) {
            B[i][j]=p2[i*j];
            rep(a,0,i) {
                rep(b,0,j) if(a!=i||b!=j) {
                    (B[i][j]-=C(i,a)*C(j,b)%P*B[a][b]%P)%=P;
                }
            }
            B[i][j]=(B[i][j]%P+P)%P;
        }
    }
    while(~scanf("%d%d",&n,&m)) printf("%lld\n",B[n][m]);
}

\[ \ \]

\[ \ \]

\[ \ \]

K: Count the Grid

看起来非常吓人,但其实还好

总体思想就是给定每个点一个\(lim\)值,求出矩阵在限制条件下的方案数容斥

然后就是二进制枚举,对于每个被枚举到的\(lim\)值要减一

其实就是把选出的数全部在\(lim\)值以下的方案减去,这样得到的就是最大值为\(lim\)的方案了

如何统计矩阵的\(lim\)值,直接离散然后循环赋值就好了,但是由于被卡常了所以我不得不加了一些优化

代码写的比较诡异不建议看,拿去对拍可以

#include<bits/stdc++.h>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a;i<=b;++i)
#define drep(i,a,b) for(reg int i=a;i>=b;--i)



char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=110,P=1e9+7;


int n,m,k,q;
int hx[N],cx,hy[N],cy;

struct REC{
    int x1,y1,x2,y2,lim;
    void Get() {
        x1=rd(),y1=rd();
        x2=rd(),y2=rd();
        hx[++cx]=x1,hx[++cx]=x2;
        if(x1>1) hx[++cx]=x1-1;
        if(x1<n) hx[++cx]=x1+1;
        if(x2>1) hx[++cx]=x2-1;
        if(x2<n) hx[++cx]=x2+1;
        hy[++cy]=y1,hy[++cy]=y2;
        if(y1>1) hy[++cy]=y1-1;
        if(y1<m) hy[++cy]=y1+1;
        if(y2>1) hy[++cy]=y2-1;
        if(y2<m) hy[++cy]=y2+1;
        lim=rd();
    }
    void Hash() {
        x1=lower_bound(hx+1,hx+cx+1,x1)-hx;
        x2=lower_bound(hx+1,hx+cx+1,x2)-hx;
        y1=lower_bound(hy+1,hy+cy+1,y1)-hy;
        y2=lower_bound(hy+1,hy+cy+1,y2)-hy;
    }
}R[N];

inline ll qsm(reg ll x,reg int k){
    reg ll res=1;
    for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
    return res;
}



int a[N][N],b[N][N],vis[N][N];
ll c[2][N][N];
ll Solve() {
    ll ans=1;
    rep(i,1,cx) rep(j,1,cy) a[i][j]=k;
    for(reg int i=0; i<q; ++i) rep(x,R[i].x1,R[i].x2) rep(y,R[i].y1,R[i].y2) a[x][y]=min(a[x][y],R[i].lim);
    rep(i,1,cx) rep(j,1,cy) ans=ans*qsm(a[i][j],(hx[i]-hx[i-1])*(hy[j]-hy[j-1]))%P;
    return ans;
}


int main(){
    int T=rd();
    rep(kase,1,T){
        n=rd(),m=rd(),k=rd(),q=rd();
        hx[cx=1]=n,hy[cy=1]=m;
        hx[++cx]=1,hy[++cy]=1;
        rep(i,0,q-1) R[i].Get();
        sort(hx+1,hx+cx+1),sort(hy+1,hy+cy+1);
        cx=unique(hx+1,hx+cx+1)-hx-1,cy=unique(hy+1,hy+cy+1)-hy-1;
        rep(i,0,q-1) R[i].Hash();
        rep(i,1,cx) rep(j,1,cy) b[i][j]=k;
        for(reg int i=0; i<q; ++i) rep(x,R[i].x1,R[i].x2) rep(y,R[i].y1,R[i].y2) b[x][y]=min(b[x][y],R[i].lim);
        rep(i,1,cx) rep(j,1,cy){
             c[0][i][j]=qsm(b[i][j],(hx[i]-hx[i-1])*(hy[j]-hy[j-1]))%P;
             c[1][i][j]=qsm(b[i][j]-1,(hx[i]-hx[i-1])*(hy[j]-hy[j-1]))%P;
             a[i][j]=b[i][j];
        }
        int A=(1<<q)-1;
        ll ans=0;
        rep(S,0,A) {
            int cnt=0;
            rep(i,0,q-1) if(S&(1<<i)) {
                cnt^=1;
                rep(x,R[i].x1,R[i].x2) rep(y,R[i].y1,R[i].y2) if(a[x][y]==R[i].lim) a[x][y]=R[i].lim-1,vis[x][y]=R[i].lim;
            }
            ll res=1;
            rep(i,1,cx) rep(j,1,cy) res=res*c[b[i][j]-a[i][j]][i][j]%P;
            if(cnt) ans-=res;
            else ans+=res;
            //cout<<S<<"  "<<Solve()<<endl;
            rep(i,0,q-1) if(S&(1<<i)) rep(x,R[i].x1,R[i].x2) rep(y,R[i].y1,R[i].y2) if(vis[x][y]) a[x][y]=vis[x][y],vis[x][y]=0;
        }
        ans=(ans%P+P)%P;
        printf("Case #%d: %lld\n",kase,ans);
    }
}

\[ \ \]

\[\ \]

L: Visible Trees

和前面的好几道题基本同理

#include<bits/stdc++.h>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)



char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=1e5+10,P=1e9+7;


int n,m,k,q;
vector <int> fac[N];

ll Solve(int n,int m) {
    ll res=0;
    rep(i,1,n) {
        int k=fac[i].size();
        int x=min(i,m);
        rep(S,0,(1<<k)-1) {
            int cnt=0,t=1;
            rep(j,0,k-1) if(S&(1<<j)) t*=fac[i][j],cnt^=1;
            if(cnt) res-=x/t;
            else res+=x/t;
        }
    }
    //cout<<res<<endl;
    return res;
}






int main(){
    rep(i,2,N-1) if(!fac[i].size()) for(int j=i;j<N;j+=i) fac[j].push_back((int)i);
    rep(kase,1,rd()){
        n=rd(),m=rd();
        ll ans=Solve(n,m)+Solve(m,n)-1;
        printf("%lld\n",ans);
    }
}


\[ \ \]

\[ \ \]

M: A Simple Chess

跳马?

首先我们讨论在空的\(n,m\)棋盘上跳的方案数

由于必须是前向跳,所以每次坐标都会增加,设横着跳\(a\)次,纵着跳\(b\)

则有

\[2*a+b=n\]

\[2*b+a=m\]

解得方程的两根即可得到a,b的值,若不是整数则无解

然后其实方案数就是\(C(a+b,a)\)

容斥的过程依旧是dp转移,每次转移时会多经过一个障碍点,容斥系数取反

但是由于这题范围较大,组合数不好求,鉴于模数只有110119,所以可以用\(Lucas\)定理

\(C(n,m) mod \ p =C(n \ mod \ p,m \ mod \ p)*(n/p,m/p) mod \ p\)

预处理后直接做

#include<bits/stdc++.h>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)



char IO;
ll rd(){
    ll s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=110,P=110119;


ll n,m;
int r;

struct NODE {
    ll x,y;
    bool operator < (const NODE __ )const{
        return x<__.x;
    }
    bool operator == (const NODE __) const{
        return x==__.x&&y==__.y;
    }
}A[N];
int k;

ll po[P]={1},Inv[P]={1,1};

ll C(ll n,ll m){
    if(n<0||m<0||n<m) return 0;
    if(n<P&&m<P) return po[n]*Inv[m]%P*Inv[n-m]%P;
    return C(n%P,m%P)*C(n/P,m/P)%P;
}




ll Calc(ll n,ll m) {
    if((n+m-2)%3!=0) return 0;
    if((2*n-m-1)%3!=0) return 0;
    ll a=(n+m-2)/3,b=(2*n-m-1)/3;
    return C(a,b);
}


//(i+j-2)/3
//(2*i-j-1)/3

ll dp[N];
int kase;
int main(){
    rep(i,1,P-1) po[i]=po[i-1]*i%P;
    rep(i,2,P-1) Inv[i]=(P-P/i)*Inv[P%i]%P;
    rep(i,1,P-1) Inv[i]=Inv[i-1]*Inv[i]%P;
    while(~scanf("%lld%lld%d",&n,&m,&r)) {
        k=0;
        A[++k]=(NODE){1ll,1ll};
        int f=0;
        rep(i,1,r) {
            ll x=rd(),y=rd();
            A[++k]=(NODE){x,y};
            if((x==n&&y==m)||(x==1&&y==1)) f=1;
        }
        if(f) {
            printf("Case #%d: 0\n",++kase);
            continue;
        }
        if(n==1&&m==1) {
            printf("Case #%d: 1\n",++kase);
            continue;
        }
        A[++k]=(NODE){n,m};
        sort(A+1,A+k+1);
        k=unique(A+1,A+k+1)-A-1;
        memset(dp,0,sizeof dp);
        dp[1]=1;
        rep(i,1,k) rep(j,i+1,k) if(A[j].x>A[i].x&&A[j].y>A[i].y) (dp[j]-=dp[i]*Calc(A[j].x-A[i].x+1,A[j].y-A[i].y+1)%P)%=P;
        ll ans=dp[k];
        ans=(P-ans)%P;
        ans=(ans%P+P)%P;
        printf("Case #%d: %lld\n",++kase,ans);
    }
}


\[ \ \]

\[ \ \]

N: TrickGCD

看到这个题就想到对于整个序列的\(gcd\)的值容斥

如何快速求出某个值限制下的序列方案数呢?

直接前缀和,\(n \ln n\)枚举,快速幂即可,最后乘一个莫比乌斯系数

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<iostream>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar())) ;
    return f?-s:s;
}

const int N=2e5+10,P=1e9+7;


int n;
int a[N];

inline ll qsm(reg ll x,reg int k) {
    if(x==1||k<=0) return 1;
    reg ll res=1;
    for(;k;k>>=1,x=x*x%P) ((k&1)&&(res=res*x%P));
    return res;
}

int pri[N],cp;
int notpri[N];
int mo[N];

int main(){
    rep(i,2,N-1)  {
        if(!notpri[i]) pri[++cp]=i,mo[i]=1;
        rep(j,1,cp){
            int t=i*pri[j];
            if(t>=N) break;
            notpri[t]=1;
            if(i%pri[j]==0) {
                mo[t]=0;
                break;
            }
            mo[t]=-mo[i];
        }
    }
    int T=rd();
    rep(kase,1,T) {
        int Up=1e9,ma=0;
        scanf("%d",&n);
        rep(i,1,n) {
            int x; scanf("%d",&x);
            a[x]++;
            Up=min(Up,x); 
            ma=max(ma,x);
        }
        rep(i,Up,N-1) a[i]+=a[i-1];
        ll ans=0;
        for(reg int i=2;i<=Up;++i) if(mo[i]) {
            ll res=1;
            rep(j,1,ma/i) {
                res=res*qsm(j,a[(j+1)*i-1]-a[j*i-1])%P;
            }
            ans+=mo[i]*res;
        }
        rep(i,Up,N-1) a[i]=0;
        ans=(ans%P+P)%P;
        printf("Case #%d: %lld\n",kase,ans);
    }
}

\[ \ \]

\[ \ \]

O: Puzzled Elena

大水题

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar())) ;
    return f?-s:s;
}

const int N=1e5+10,P=1e9+7;


int n;
struct Edge{
    int to,nxt;
}e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v){
    e[++ecnt]=(Edge){v,head[u]};
    head[u]=ecnt;
}

vector <int> fac[N];

int a[N],ans[N];
int c[N];
void dfs(int u,int f) {
    int n=fac[a[u]].size();
    ans[u]=0;
    rep(S,0,(1<<n)-1) {
        int t=1,cnt=0;
        rep(i,0,n-1) if(S&(1<<i)) cnt^=1,t*=fac[a[u]][i];
        if(cnt) ans[u]+=c[t];
        else ans[u]-=c[t];
    }
    rep(S,0,(1<<n)-1) {
        int t=1;
        rep(i,0,n-1) if(S&(1<<i)) t*=fac[a[u]][i];
        c[t]++;
    }
    for(int i=head[u];i;i=e[i].nxt) {
        int v=e[i].to;
        if(v==f) continue;
        dfs(v,u);
    }
    rep(S,0,(1<<n)-1) {
        int t=1,cnt=0;
        rep(i,0,n-1) if(S&(1<<i)) cnt^=1,t*=fac[a[u]][i];
        if(cnt) ans[u]-=c[t];
        else ans[u]+=c[t];
    }
}




int kase;
int main(){ 
    rep(i,2,N-1) if(!fac[i].size()) for(int j=i;j<N;j+=i) fac[j].push_back((int)i);
    while(~scanf("%d",&n)) {
        memset(c,0,sizeof c);memset(head,0,sizeof head);ecnt=0;
        rep(i,2,n) {
            int u=rd(),v=rd();
            AddEdge(u,v);
            AddEdge(v,u);
        }
        rep(i,1,n) a[i]=rd();
        dfs(1,0);
        printf("Case #%d:",++kase);
        rep(i,1,n) printf(" %d",ans[i]);
        puts("");
    }
}

\[ \ \]

\[ \ \]

P: Just Random

\(O(1)\) 的题为甚么在最后一题

对于两个数\(n,m(n \leq m)\),它们累和为\(x\)的方案数其实是有一定规律的

\(x\leq n\)时,方案数为\(x+1\)

\(n\leq x \leq m\)时,方案数为\(n+1\)

\(m\leq x\)时,方案数为\(max\{0,n+m-x+1\}\)

然后就ojbk了

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar())) ;
    return f?-s:s;
}

const int N=1e5+10;


ll a,b,c,d;
ll p,m;


ll Solve(ll a,ll b) {
    if(a<0||b<0) return 0;
    if(a>b) swap(a,b);
    ll ans=0,t;
    if(a>=m) {
        t=1ll*(a-m)/p*p+m;
        ans+=1ll*(m+t+2)*((t-m)/p+1)/2;
    }
    t=0;
    if(b>=m) t=(b-m)/p+1;
    if(a>=m) t-=(a-m)/p+1;
    ans+=t*(a+1);
    if(b>=m) {
        t=1ll*(b-m)/p*p+m;
        ans-=1ll*(2*a+2*b-m-t+2)*((t-m)/p+1)/2;
    }
    if(a+b>=m) {
        t=1ll*(a+b-m)/p*p+m;
        ans+=1ll*(2*a+2*b-m-t+2)*((t-m)/p+1)/2;
    }
    return ans;
}

ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); }



int main(){ 
    int cnt=0;
    rep(i,0,0) rep(j,0,5) if((i+j)%3==2) cnt++;
    rep(kase,1,rd()) {
        a=rd(),b=rd(),c=rd(),d=rd(),p=rd(),m=rd();
        ll ans=Solve(b,d)-Solve(a-1,d)-Solve(b,c-1)+Solve(a-1,c-1);
        printf("Case #%d: ",kase);
        if(!ans) {
            puts("0/1");
            continue;
        }
        ll t=1ll*(b-a+1)*(d-c+1);
        ll g=gcd(t,ans);
        ans/=g,t/=g;
        printf("%lld/%lld\n",ans,t);
    }
}

猜你喜欢

转载自www.cnblogs.com/chasedeath/p/11403449.html