人生第一次A,B層一塊考rank2,雖說分差沒幾分,但仍是值得記念。c++
T1 天空龍:ide
大神題,由於我從不寫快讀也沒有寫考場註釋的習慣,因此不會作,全hzoi就kx會作,kx真大神級人物。函數
T2 巨神兵:spa
大神題,一看數據範圍這麼小,咱們考慮狀壓,最傻逼的暴力思路是壓邊,可是這顯然不行。正解是壓點,設$f[s]$爲當前選定點集狀態爲$s$的方案數。code
咱們考慮轉移,當前選定的點集確定是能夠經過邊和沒有連過來的點相連構成新的方案。因此轉移因此咱們考慮枚舉補集的子集$k$,設$cnt$爲s與k兩個點集相連的邊數,那麼轉移爲$f[s|k]+=f[s]*2^{cnt}$?不,這樣會算重,由於好比咱們先加A點,再加B點,和先加B點,再加A點是同樣的。因此考慮容斥,容斥係數爲$(-1)^{size[k]+1}$,蒟蒻博主不會正着推,可是這個能夠證實是對的。咱們設$g[i]$爲轉移了$i$層的係數,那麼顯然$g[0]=1$,而後$g$的遞推式爲$g[i]=\sum_{j=1}^{i}{C_{j}^{i}*(-1)^{j+1}*g[i-j]}$,咱們要證的是$g[i]=1$,首先$g[i-j]$能夠消掉,由於你由3層轉移到5層,和從0層轉移到2層是同樣的而後blog
$\sum_{j=1}^{i}{C_j^i*(-1)^{j+1}}$it
$=(-1)*(\sum_{j=0}^{i}{C_j^i*(-1)^{j}}-C_j^0*(-1)^0)$io
$=(-1)*((1-1)^i-1)=1$ 證畢。event
而後轉移用了不少預處理,主要是找兩個點集相連的邊,咱們首先預處理每一個點與之相連點的狀態,將它與上當前枚舉的補集就好,而後還要預處理的是,每一個二進制數中1的個數和每一個取出來的1,是第幾位,而後dp就行了。class
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 const int N=1900000,mod=1e9+7; 5 int first[N],nex[N],to[N],tot,rc[N],sta[N],ma[5242880],f[5242880],n,m,qpow[N],re[N]; 6 void add(int a,int b){ 7 to[++tot]=b,nex[tot]=first[a],first[a]=tot; 8 } 9 signed main(){ 10 scanf("%lld%lld",&n,&m); 11 for(int i=1;i<=m;++i){ 12 int x,y; 13 scanf("%lld%lld",&x,&y); 14 add(x,y); 15 } 16 for(int i=0;i<=(1<<n);++i){ 17 ma[i]=ma[i>>1]+(i&1); 18 } 19 // for(int i=1;i<=1<<n;++i) cout<<"ma["<<i<<"]=="<<ma[i]<<" ";cout<<endl; 20 f[0]=qpow[0]=1; 21 for(int i=1;i<=n*n;++i) qpow[i]=(qpow[i-1]<<1)%mod; 22 for(int i=0;i<=n+1;++i) rc[i]=(i&1)?-1:1; 23 // for(int i=0;i<=n+1;++i) cout<<rc[i]<<" ";cout<<endl; 24 for(int i=1;i<=n;++i){ 25 re[1<<i-1]=i; 26 for(int j=first[i];j;j=nex[j]){ 27 sta[i]|=1<<to[j]-1; 28 } 29 } 30 int Max=1<<n,cnt=0,maxn=Max-1; 31 for(register int s=0;s^Max;++s){register int kl=~s; 32 for(register int i=kl&maxn;i;i=(i-1)&kl){ 33 cnt=0; 34 for(register int j=s;j;j-=j&-j) cnt+=ma[sta[re[j&-j]]&i]; 35 // cout<<cnt<<" "; 36 // cout<<f[s]%mod*qpow[cnt]%mod<<" "; 37 (f[s|i]+=rc[ma[i]+1]*f[s]%mod*qpow[cnt]%mod)%=mod; 38 } 39 } 40 printf("%lld\n",(f[maxn]+mod)%mod); 41 }
T3 太陽神:
正難則反,lcm大於n的很差求,咱們能夠考慮求小於n的,即$n^2-\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}{[lcm(i,j)<=n]}$
而後再轉化$n^2-\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}{[\frac{i\times j}{gcd(i,j)}<=n]}$,難點在於求後面的那部分,如今咱們考慮枚舉,$i,j$的最大公約數d,柿子就變成了$\sum\limits_{d=1}^{n}\sum\limits_{a=1}^{\frac{n}{d}}\sum\limits_{b=1}^{\frac{n}{d}}{[gcd(a,b)==1][a\times b\leq \frac{n}{d}]}$
咱們考慮求$f_k=\sum\limits_{1\leq a,b\leq n}{[a\times b\leq k][gcd(a,b)==1]}$,咱們設$S_k=\sum\limits_{1\leq a,b\leq n}{[a\times b\leq k]}$,這一個數論分塊就能夠求出,那麼$f_k=S_k-\sum\limits_{t>1} f_{\frac{k}{t^2}}$,前面S函數是不考慮gcd爲1的條件的,後面的t至關因而枚舉a,b的因子,同時把a,b因子提出便可獲得$\frac{k}{t^2}$,而後就是關於f函數的求法,把它差分一下獲得g[k],那麼g[k]的含義就是$\sum\limits [a\times b=k][gcd(a,b)=1]$的a,b對數
$g[k]=2^cnt$,cnt爲k的質因子種數,由於每種質因子,只能所有給a或b,而不能一部分給a,一部分給b,由於那樣的話$gcd(a,b)$就不是1,這樣f線篩便可,當n較小時直接用線篩篩出來的,當n較大時用上面提到的方法迭代便可。最外層數論分塊,時間複雜度O(玄學)$O(n^{\frac{2}{3}})$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 const int N=1e7+10,mod=1e9+7; 5 int v[N],prime[N],num,f[N],n; 6 void init(){ 7 f[1]=1; 8 for(int i=2;i<=10000000;++i){ 9 if(!v[i]){ 10 prime[++num]=i; 11 f[i]=2; 12 v[i]=1; 13 } 14 for(int j=1;j<=num&&i*prime[j]<=10000000;++j){ 15 v[i*prime[j]]=1; 16 if(i%prime[j]==0){ 17 f[i*prime[j]]=f[i]; 18 break; 19 } 20 f[i*prime[j]]=f[i]*f[prime[j]]%mod; 21 // cout<<f[i]<<" "<<f[prime[j]]<<" "<<f[i*prime[j]]<<endl; 22 } 23 } 24 } 25 int calS(int x){ 26 int ans=0; 27 for(int l=1,r;l<=x;l=r+1){ 28 r=x/(x/l); 29 (ans+=(r-l+1)*(x/l)%mod)%=mod; 30 } 31 return ans; 32 } 33 34 int calF(int x){//cout<<x<<" "<<endl; 35 if(x<=10000000) return f[x]; 36 int ans=0; 37 ans=calS(x)%mod; 38 for(int i=2;i*i<=x;++i){ 39 (((ans-=calF(x/(i*i))+mod)%=mod)+=mod)%=mod; 40 } 41 return ans; 42 } 43 44 signed main(){ 45 scanf("%lld",&n); 46 init(); 47 //for(int i=1;i<=20;++i) cout<<"f["<<i<<"]=="<<f[i]<<" ";cout<<endl; 48 for(int i=1;i<=10000000;++i) (f[i]+=f[i-1])%=mod; 49 int ans=(n%mod*(n%mod))%mod; 50 for(int i=1,r;i<=n;i=r+1){ 51 r=n/(n/i); 52 // cout<<ans<<" "; 53 // cout<<i<<" "<<r<<endl; 54 (((ans-=calF(n/i)%mod*(r-i+1)%mod+mod)%=mod)+=mod)%=mod; 55 // cout<<calF(n/i)%mod*(r-i+1)%mod+mod<<endl; 56 } 57 printf("%lld",(ans+mod)%mod); 58 }