题目大意
你要构造 n 个数,满足 m 个限制,每个限制条件给出两个长度相等的区间,表示这两个区间的数要一样。比如限制是 [1, 3] 和 [3, 5],那 {1, 2, 1, 2, 1} 就是满足条件的。
求方案数。
n≤105
题解
这看上去,就很并查集。。。
暴力的并查集就是对于每个限制,枚举区间的每个数,合并。
改进一下就是用 ST 表,ST 表共 O(logn) 层,每层 O(n) 个元素,给这总共 O(nlogn) 个元素维护并查集。对于区间 [l1,r1] 要跟 [l2,r2] 合并,找到最大的 2t≤r1−l1+1,然后在并查集上合并 [l1,l1+2t−1] 与 [l2,l2+2t−1]、[r1−2t+1,r1] 与 [r2−2t+1,r2]。最后从大到小遍历一遍 ST 表,对于区间 [l,r],让 [l,mid] 和 [mid+1,r] 分别连向 [l,r] 的并查集根对应的两个子区间。
相关题目
GDOI2016 回文树:改进一下然后放到树上。(假老师出考场怒 D 这题)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include<cmath> #include<cstdio> #include<algorithm> #define fo(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) using namespace std;
typedef long long LL;
const int maxn=1e5+5, MX=17; const LL mo=1e9+7;
int n,m;
int ga[maxn*MX+5]; int get(int x) {return (ga[x]==x) ?x :ga[x]=get(ga[x]) ;}
int Log[maxn],bh[maxn][MX+5],sum; void rmq_pre() { fo(i,1,n) Log[i]=log(i)/log(2); fo(j,0,MX) { int len=1<<j; fo(i,1,n-len+1) bh[i][j]=++sum; } fo(i,1,sum) ga[i]=i; }
LL mi(LL x,LL y) { LL re=1; for(; y; y>>=1, x=x*x%mo) if (y&1) re=re*x%mo; return re; }
int fir[maxn*MX+5]; int main() { scanf("%d %d",&n,&m); rmq_pre(); while (m--) { int l1,r1,l2,r2; scanf("%d %d %d %d",&l1,&r1,&l2,&r2); if (l1>l2) swap(l1,l2), swap(r1,r2); int t=Log[r1-l1+1]; ga[get(bh[l2][t])]=get(bh[l1][t]); ga[get(bh[r2-(1<<t)+1][t])]=get(bh[r1-(1<<t)+1][t]); } fd(j,MX,1) { int len=1<<j; fo(i,1,n-len+1) if (!fir[get(bh[i][j])]) fir[get(bh[i][j])]=i; else { int x=fir[get(bh[i][j])]; ga[get(bh[x][j-1])]=get(bh[i][j-1]); ga[get(bh[x+len/2][j-1])]=get(bh[i+len/2][j-1]); } } LL ans=1; fo(i,1,n) if (get(bh[i][0])==bh[i][0]) ans=ans*10%mo; ans=ans*mi(10,mo-2)%mo *9%mo; printf("%lld\n",ans); }
|