1. 程式人生 > >BZOJ 2393 淺談題目性質深度挖掘及容斥原理DFS寫法

BZOJ 2393 淺談題目性質深度挖掘及容斥原理DFS寫法

這裡寫圖片描述
(助威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 */

嗯,就是這樣