zoukankan      html  css  js  c++  java
  • 容斥专题总结

    容斥专题总结

    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);
    	}
    }
    
    
  • 相关阅读:
    (转)十分钟搞定CSS选择器
    (转)我所理解的OOP——UML六种关系
    闲话:你今天OO了吗?
    oledb方式读取excel文件
    (转)asp.net 高质量缩略图
    (转载)重温SQL——行转列,列转行
    第九讲,资源表解析
    第八讲,TLS表(线程局部存储)
    第七讲,重定位表
    第六讲,导出表
  • 原文地址:https://www.cnblogs.com/chasedeath/p/11403449.html
Copyright © 2011-2022 走看看