[SCOI 2016]萌え萌え萌え(倍増+そして調査集)
2382 ワード
区間([a,b])と([c,d])が等しい場合.2つの区間に対応する位置の数を集計した.最后にセットの数を调べて(num)答えは(9*10^num)個数なので、前置(0)はありません.しかし、2つの区間に対応する位置の数があり、集計を調べるのに時間がかかりすぎる.どうしよう.乗数の使用を検討します.私たちは((i,j))を用いて([i,i+(1<この区間の後、どの区間でも最大(log)個のような倍増区間を表す.そして、私たちは倍増区間の大きさによって大きなものから小さいものへ列挙する.((x,i))と((y,i))が1つの並列調査セットにあるとき、((x,i-1))と((y,i-1))が1つの並列調査セットにある.((x+(1<と((y+(1<も1つの並列調査セットにあります.この2対の倍増区間を並列調査セットし、最後に長さ(1)の要素を並列調査セットします.複雑度は(O(nlogn))である.突っ込み問題
転載先:https://www.cnblogs.com/Xu-daxia/p/10520086.html
#include
#include
#include
#include
#include
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=101000;
int fa[N*22],id[N][22],pw[22],n,m,ans,book[N],tot,xb[N*22],l[N*22];
int find(int x){
if(fa[x]==x)return x;
else return fa[x]=find(fa[x]);
}
int ksm(int x,int b){
int tmp=1;
while(b){
if(b&1)tmp=tmp*x%mod;
b>>=1;
x=x*x%mod;
}
return tmp;
}
int read(){
int sum=0,f=1;char ch=getchar();
while(ch'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
void pre_work(int x){
pw[0]=1;
for(int i=1;i<=20;i++)pw[i]=pw[i-1]*2;
int len=log2(x);
for(int j=0;j<=len;j++)
for(int i=1;i+pw[j]-1<=n;i++)
id[i][j]=++tot,xb[tot]=i,l[tot]=j,fa[tot]=tot;
}
signed main(){
n=read();m=read();
pre_work(n);
while(m--){
int a=read(),b=read();
int c=read(),d=read();
for(int i=20;i>=0;i--)
if(a+pw[i]-1<=b){
int x=find(id[a][i]),y=find(id[c][i]);
if(x!=y)fa[x]=y;
a=a+pw[i];c=c+pw[i];
}
}
int len=log2(n);
for(int j=len;j>=1;j--)
for(int i=1;i+pw[j]-1<=n;i++){
int f=find(id[i][j]);
int x=find(id[i][j-1]);
int y=find(id[xb[f]][l[f]-1]);
if(x!=y)fa[x]=y;
x=find(id[i+pw[j-1]][j-1]);
y=find(id[xb[f]+pw[l[f]-1]][l[f]-1]);
if(x!=y)fa[x]=y;
}
for(int i=1;i<=n;i++){
int x=find(id[i][0]);
if(book[x]==0)ans++,book[x]=1;
}
printf("%lld",9ll*ksm(10,ans-1)%mod);
return 0;
}
転載先:https://www.cnblogs.com/Xu-daxia/p/10520086.html