[BZOJ4569] [Luogu 3295] [SCOI2016]ピリダジンMengmeng(互いに素なセット乗算+)
フェイス質問
そこ進数N(リーダーレス0)、mは制限の限界ストリップ付与されている((L_1、R_1、L_2 \を (R_2) )保証R_1、R_2 L_1 =-L_2は、 \) の数を表します。最初の\([L_1、R_1] \ ) のビット\([L_2、R_2] \ ) 同一のビット。Q.条件をどのように多く、このような数字満たすために、回答剰余\(10 ^ 9 + 7 \) 、
\(N \の当量10 ^ 5 \)
分析
表記:我々は最初のものとして知られている進数降順、最初の二つの各ビット...、示さ\(a_iを\)
最初の点、すなわち、間隔制限変換素子と等しくなるように、非常に微妙である(K + L_1 A_ {} = {A_ R_1 + K}に(K \ [0、+ L_1-R_1 1])\)\。次に、各リミット暴力の合併のためのリンクブロックなど全て等しい要素が、最終的に我々が得る\(CNT \)をユニコムブロック目。各ブロックユニコムのために、私たちはそれを同じ値を割り当てられたすべての要素を与える必要があります。含ま\は(A_1 \)ユニコムブロック、要素のみ([1,9] \)\値(最初のものは0ではないため)、他の要素は、ユニコムブロックができ、[(0,9を\します] \)内の値。乗算の原理によれば、答えは\(9 \回10 ^ { CNT-1} \)
問題はその後すぐに接続を維持する方法です。それが直接行われる\(O(N ^ 2 \アルファ(N))\) 。私たちは、1つの合併気づいた\(\ [L_1はR_1、] ) すべての要素、それは時間の無駄である、あなたは乗算の最適化を使用することができます。
、我々は確認して設定します二次元に1D \(F [I] [J] \)の間隔を表す\を([J、J + 2 ^ I-1] \) の要素で表されます。我々は、ちょうどする必要が組み合わさ(R_1-L_1 + 1 \ \ ) 例えば13に分割されるように、バイナリ分割\(3 + 2 ^ 2 ^ 2 + 2 ^ 0 \)それぞれについて。(\ J)を\、組み合わせ([J] \ F)\の長さに対応する\(2 ^ j個の\)間隔。
int len=r1-l1+1;
for(int j=log2n;j>=0;j--){//二进制拆分
if(len&(1<<j)){
S.merge(j,l1,l2);
l1+=(1<<j);
l2+=(1<<j);
}
}
[フォトmhyブログ]
以下、バイナリ分割3 \(1 + 2 ^ 2 ^ 0 \)の合わせた長さ\(2 ^ 1 \)間隔のその後組み合わせた長さ\(2 ^ 0 \)セクション
但是我们查询的时候需要查询的是每个点的信息,即\(f[0][i]\),所以我们要下推合并。合并长度为\(2^j\),起点分别为\(x,y\)的区间时,我们把区间断成两半,合并\(f[j-1][x+2^{j-1}],f[j-1][y+2^{j-1}]\),还要合并\(f[j-1][x],f[j-1][y]\)这样做\(O(\log n)\)次,就可以得到\(f[0][i]\)
for(int j=log2n;j>=1;j--){
for(int i=1;i+(1<<j)-1<=n;i++){//下推关系,长度为2^j的推到2^(j-1)
int f=S.find(j,i);
S.merge(j-1,i,f);
S.merge(j-1,i+(1<<(j-1)),f+(1<<(j-1)));
}
}
时间复杂度为\(O(n \alpha(n)\log n )\),但是这里我偷了个懒,只写了路径压缩的并查集,时间复杂度\(O(n \log^2 n)\),由于常数很小,可以通过。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define maxn 100000
#define maxlogn 21
#define mod 1000000007
using namespace std;
typedef long long ll;
inline ll fast_pow(ll x,ll k){
ll ans=1;
while(k){
if(k&1) ans=ans*x%mod;
x=x*x%mod;
k>>=1;
}
return ans;
}
struct disjoint_set{
int fa[maxlogn+5][maxn+5];//f[i][j]表示[j,j+2^i-1]的代表元
void ini(int n){
int k=log2(n)+1;
for(int j=0;j<=k;j++){
for(int i=1;i<=n;i++){
fa[j][i]=i;
}
}
}
int find(int b,int x){
if(fa[b][x]==x) return x;
else return fa[b][x]=find(b,fa[b][x]);
}
void merge(int b,int x,int y){
int fx=find(b,x);
int fy=find(b,y);
if(fx!=fy) fa[b][fx]=fy;
}
}S;
int log2n;
int n,m;
int main(){
int l1,l2,r1,r2;
scanf("%d %d",&n,&m);
log2n=log2(n)+1;
S.ini(n);
for(int i=1;i<=m;i++){
scanf("%d %d %d %d",&l1,&r1,&l2,&r2);
int len=r1-l1+1;
for(int j=log2n;j>=0;j--){//二进制拆分
if(len&(1<<j)){
S.merge(j,l1,l2);
l1+=(1<<j);
l2+=(1<<j);
}
}
}
for(int j=log2n;j>=1;j--){
for(int i=1;i+(1<<j)-1<=n;i++){//下推关系,长度为2^j的推到2^(j-1)
int f=S.find(j,i);
S.merge(j-1,i,f);
S.merge(j-1,i+(1<<(j-1)),f+(1<<(j-1)));
}
}
int cnt=0;
for(int i=1;i<=n;i++){
if(S.find(0,i)==i) cnt++;
}
printf("%lld\n",9*fast_pow(10,cnt-1)%mod);
}