BZOJ 2393 淺談題目性質深度挖掘及容斥原理DFS寫法
阿新 • • 發佈:2019-02-07
(助威TEAM WE)
世界真的很大
容斥原理這種東西~~雖然只是刷了幾道水題而已但感覺還是要總結一波
數論的複習差不多就要結束了?
希望不要耽誤太多時間吧~還要留時間給DP的第二輪複習
最後的容斥原理
看題先:
description:
~Cirno發現了一種baka數,這種數呢~只含有2和⑨兩種數字~~
現在Cirno想知道~一個區間中~~有多少個數能被baka數整除~
但是Cirno這麼天才的妖精才不屑去數啦
只能依靠聰明的你咯。
input:
一行正整數L R
( 1 < L < R < 10^10)
output:
一個正整數,代表所求的答案
從題面上來說這道題還是比較顯然的容斥了
某一個區間一堆數的倍數有多少個,嗯
關鍵是這個限制條件比較奇怪,數的每一位只有2和9
大概思路就是說,找到所有這樣的數,每一個的倍數有多少 - 沒兩個的倍數有多少 + 每3個的倍數有多少 。。。容斥
這樣的複雜度是2^n的,一共有多少個這樣的數呢?
每一位可以是2或者9,一共10位,那就是最多2^10個這樣的數
太多了點
考慮完全被覆蓋的可以不用考慮,即有2就不用考慮22,222,等等
因為在篩2的倍數的時候一定可以把22,222篩出去
我們需要的只是互相之間不是倍數關係的一堆數,用他們來容斥罷了
這樣一篩,就少得多了,可以嘗試直接容斥但是還是有點懸
考慮剪枝
如果選出的數的集合的LCM已經大於R了肯定return就好
為了最大化剪枝效率我們從大道小跑
這樣就可以玄學的卡過去了
雖然說是玄學但是這個其實也算是容斥優化的常用用法
容斥可以用DFS來實現,比起手動容斥的思路,+1,-2,+3,DFS的寫法其實是先看選那些,再從選的個數來決定是+是-
完整程式碼:
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long dnt;
int tot=0,cnt=0,mrk[100010];
dnt L,R,ans=0;
dnt a[100010],b[100010];
void init(dnt x)
{
if(x>R) return ;
if(x) a[++tot]=x;
init(x*10 +2),init(x*10+9);
}
dnt gcd(dnt a,dnt b)
{
return b==0 ? a : gcd(b,a%b);
}
dnt lcm(dnt a,dnt b)
{
return a/gcd(a,b)*b;
}
void dfs(int state,int flag,dnt delta)
{
if(state==0)
{
if(delta!=1) ans+=flag*(R/delta-(L-1)/delta);
return ;
}
dfs(state-1,flag,delta);
dnt tmp=lcm(delta,b[state]);
if(tmp<=R) dfs(state-1,-flag,tmp);
}
int main()
{
cin >> L >> R;
init(0);
sort(a+1,a+tot+1);
for(int i=1;i<=tot;i++)
{
if(mrk[i]) continue ;
b[++cnt]=a[i];
for(int j=i+1;j<=tot;j++)
if(a[j]%a[i]==0) mrk[j]=1;
}
dfs(cnt,-1,1);
cout << ans << endl;
return 0;
}
/*
EL PSY CONGROO
*/
嗯,就是這樣