容斥原理 & Mobious函数
容斥原理
此处为笔记图片
例题:Devu和鲜花
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<iomanip>
#include<cstring>
#include<string>
#define LL long long
using namespace std;
const LL mod = 1e9 + 7;
LL n, m, a[25], ans;
LL dwn = 1;
long long read(){
long long x=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*h;
}
LL power (LL a, LL b) {
LL sum = 1;
for ( ; b > 0; b >>= 1) {
if (b & 1)sum = sum * a % mod;
a = a * a % mod;
}
return sum;
}
LL inv (LL a) {
return power(a, mod - 2) % mod;
}
LL C (LL N, LL M) { // 注意数据类型!不是int
if (N > M) return 0;
if (M < 0) return 0;
LL s = 1;
for (LL i = M; i >= M - N + 1; i --) s *= (i % mod), /*处处取模好习惯!*/ s %= mod;
// cout << "-" << s << endl;
s = s * dwn % mod;
return s % mod;
}
int main() {
n = read(); m = read();
for (int i = 1; i <= n; i ++) a[i] = read();
for (int i = 1; i <= n - 1 ; i ++) dwn *= inv(i), dwn %= mod; // C(n-1,*)中n-1不变,所以可以预处理,缩短时间
for (LL i = 0; i <= (1 << n) - 1; i ++) {
LL fh = 1, sum = 0, k = i;
for (LL j = 1; k > 0; k >>= 1, j ++) {
if (k & 1) {
fh *= -1;
sum += (a[j] + 1);
}
}
// cout << sum << endl;
ans += (C(n - 1, m - sum + n - 1) * fh + mod) % mod; // 同下!
}
cout << ( ans + mod ) % mod << endl; // 注意!此处需要防止ans是负的
return 0;
}
莫比乌斯函数
笔记:[此处为图片]
例题:ZAP-Queries
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<iomanip>
using namespace std;
long long T,a,b,d,ans;
long long mb[50010];
long long pri[50010],idx=0;
bool ss[50010];
long long read(){
long long x=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*h;
}
inline long long mymin(long long a,long long b){
return (a>b ? b:a);
}
/*
这是第一种莫比乌斯函数的求法,运用的是线性筛
*/
/*
void init(){
for(long long i=1;i<=50000;i++)mb[i]=1,ss[i]=0; // 提前预处理
for(long long i=2;i<=50000;i++){ // 细节:不能sqrt(50000)
if(!ss[i]){
mb[i]=-1;
for(long long j=2;i*j<=50000;j++){
if(j%i==0)mb[i*j]=0; // 因为我更新的是i*j,如果j%i==0,那么,i*j%(i^2)一定0,mb[i*j]=0
else mb[i*j]*=-1;
ss[i*j]=1;
}
}
}
for(long long i=1;i<=50000;i++) mb[i]+=mb[i-1];
return ;
}
*/
/*
这是运用埃氏筛的求法,速度更快,细节更多
*/
void init(){
mb[1]=1; // 细节:如果写成线性筛那样,那你人没了
for(long long i=2;i<=50000;i++){
if(!ss[i]){
mb[i]=-1;
pri[++idx]=i;
}
for(long long j=1;j<=idx&&pri[j]*i<=50000;j++){
ss[i*pri[j]]=1;
if(i%pri[j]==0)break;
else mb[i*pri[j]]=-mb[i]; // 细节:这里一定要注意理解,只有所有质因子幂次数为1的数,才会进行else运算
// 例如 18 :它是i=9时被标记的,虽然9%2=1,但是mb[9]本身为0,所以进行了也没个卵用
// 9会在i=3时标记,因为3%3=0,所以mb[9]=0
// 因为那些质因子幂数大于一的数我不会对其的mb值更新,
// 所以一开始只能写个mb[1]=1;,而不能写成线性筛那样
}
}
for(long long i=1;i<=50000;i++) mb[i]+=mb[i-1];
return ;
}
int main(){
init();
scanf("%lld",&T);
while(T--){
ans=0;
scanf("%lld %lld %lld",&a,&b,&d);
a=a/d; b=b/d;
long long MIN=mymin(a,b);
for(long long i=1;i<=MIN; ){
long long nxt=mymin(a/(a/i),b/(b/i)); // 节约时间,g(x)=floor(a/floor(a/x)),但跳的时候一定要保证floor(a/i)*floor(b/i)一样
// 所以要取Min
nxt=mymin(MIN,nxt);
ans+=(mb[nxt]-mb[i-1])*((a/i)*(b/i));
i=nxt+1;
}
printf("%lld\n",ans);
}
return 0;
}

浙公网安备 33010602011771号