zoukankan      html  css  js  c++  java
  • LOJ3267 Help Yourself

    Help Yourself

    Bessie 现在有 (N) 条在一条数轴上的线段,第 (i) 条线段覆盖了 ([l_i,r_i]) 的所有实数。

    定义一个线段集合的为所有至少被一条线段覆盖的实数。定义一个线段集合的复杂度为该集合并的联通块个数的 (K) 次方。

    Bessie 现在想计算这 (N) 条线段的 (2^N) 个子集的复杂度之和模 (10^9+7)。通常你的任务是帮 Bessie 进行计算,但是这次你是 Bessie,而且没人能帮你,帮帮你自己吧!

    对于 (100\%) 的数据,有 (1le N le 10^5,~2le Kle 10,~1le l_i<r_ile 2N)

    题解

    http://jklover.hs-blog.cf/2020/06/06/Loj-3267-Help-Yourself/#more

    第二类斯特林数 + 线段树优化 dp 转移.

    USACO 怎么也有如此套路的题…

    可以先将所有线段按照左端点从小到大排序,方便后续处理.

    记一个非空集合 (T) 的线段形成的连通块数目为 (c(T)) ,则答案为 (sum_T c(T)^k) .

    朴素的 dp

    (dp(i,x,p)) 表示考虑了前 (i) 条线段,选了的线段右端点最大值为 (x) ,有 (p) 个连通块的方案数.

    这样做状态数为 (O(n^3)) ,不太可行.

    幂次展开为组合数

    注意到 (k​) 比较小,尝试将其展开为组合数的形式,

    [egin{aligned} ans&=sum_T c(T)^k \ &=sum_Tsum_{i=0}^k {krace i}cdot i!cdot inom{c(T)}{i}\ &=sum_{i=0}^k{krace i}cdot i!cdot sum_{T}inom{c(T)}{i} end{aligned} ]

    当题目中要算 (c^k) 的贡献, (c) 大而 (k) 小,尤其是 (c) 是某种东西的数目时,可以尝试展开成组合数.

    原来需要记录 (c) 的大小,最后算贡献,现在就将贡献摊在每个 (c) 增大的时候计算,只用记录 (k) 的大小.

    于是我们在状态中就不需要记录连通块数目,而是在新产生一个连通块时,考虑它是否被选.

    (dp(i,x,p)) 表示考虑了前 (i) 条线段,选了的线段右端点最大值为 (x) ,产生的连通块被选定了 (p) 个的方案数.

    状态数从 (O(n^3)) 降到了 (O(n^2k)) ,还需进一步优化.

    线段树优化转移

    考虑转移的形式,假定当前在考虑第 (i) 条线段,其覆盖的区间为 ([l,r]) .

    若不选这条线段,则每个 (dp(i-1,x,p)) 转移到 (dp(i,x,p)) .

    若选了这条线段,则根据 (x​) 的大小分情况讨论.

    (x<l​) ,此时会产生新的一个连通块, (dp(i-1,x,p)​) 可以转移到 (dp(i,r,p)​)(dp(i,r,p+1)​) .

    (lle xle r​) , (dp(i-1,x,p)​) 可以转移到 (dp(i,r,p)​) .

    (x>r) , (dp(i-1,x,p)) 可以转移到 (dp(i,x,p)) .

    不难发现,我们可以开 (k+1) 棵线段树来维护这个 dp 数组,第 (p) 棵线段树维护了当前所有的 (dp(i,x,p)) .

    (i)通过for枚举,(p)通过(k+1)棵线段树维护,(x)通过线段树下标维护。

    时间复杂度 (O(nklog n)) .

    CO int N=2e5+10;
    
    struct Seg{
    	int sum[4*N],tag[4*N];
    	
    	#define lc (x<<1)
    	#define rc (x<<1|1)
    	#define mid ((l+r)>>1)
    	IN void push_up(int x){
    		sum[x]=add(sum[lc],sum[rc]);
    	}
    	IN void put_tag(int x,int v){
    		sum[x]=mul(sum[x],v),tag[x]=mul(tag[x],v);
    	}
    	IN void push_down(int x){
    		if(tag[x]!=1){
    			put_tag(lc,tag[x]),put_tag(rc,tag[x]);
    			tag[x]=1;
    		}
    	}
    	void insert(int x,int l,int r,int p,int v){
    		if(l==r) {sum[x]=add(sum[x],v); return;}
    		push_down(x);
    		if(p<=mid) insert(lc,l,mid,p,v);
    		else insert(rc,mid+1,r,p,v);
    		push_up(x);
    	}
    	void modify(int x,int l,int r,int ql,int qr,int v){
    		if(ql>qr) return;
    		if(ql<=l and r<=qr) return put_tag(x,v);
    		push_down(x);
    		if(ql<=mid) modify(lc,l,mid,ql,qr,v);
    		if(qr>mid) modify(rc,mid+1,r,ql,qr,v);
    		push_up(x);
    	}
    	int query(int x,int l,int r,int ql,int qr){
    		if(ql>qr) return 0;
    		if(ql<=l and r<=qr) return sum[x];
    		push_down(x);
    		if(qr<=mid) return query(lc,l,mid,ql,qr);
    		if(ql>mid) return query(rc,mid+1,r,ql,qr);
    		return add(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr));
    	}
    	#undef lc
    	#undef rc
    	#undef mid
    }T[11];
    
    pair<int,int> seg[N];
    int S[11][11];
    
    int main(){
    	int n=read<int>(),K=read<int>();
    	for(int i=1;i<=n;++i) read(seg[i].first),read(seg[i].second);
    	sort(seg+1,seg+n+1);
    	S[0][0]=1;
    	for(int i=1;i<=K;++i)for(int j=1;j<=i;++j)
    		S[i][j]=add(S[i-1][j-1],mul(S[i-1][j],j));
    	T[0].insert(1,0,2*n,0,1);
    	for(int i=1;i<=n;++i){
    		int l=seg[i].first,r=seg[i].second;
    		for(int p=K;p>=0;--p){
    			if(p<K) T[p+1].insert(1,0,2*n,r,T[p].query(1,0,2*n,0,l-1));
    			T[p].insert(1,0,2*n,r,T[p].query(1,0,2*n,0,r));
    			T[p].modify(1,0,2*n,r+1,2*n,2);
    		}
    	}
    	int ans=0,fac=1;
    	for(int p=0;p<=K;++p){
    		ans=add(ans,mul(T[p].sum[1],mul(S[K][p],fac)));
    		fac=mul(fac,p+1);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Ansible快速实战指南----多机自动化执行命令、部署神器
    linux 挂载共享盘
    蓄水池抽样算法
    PCA MATLAB代码
    网口转串口
    通过Python收集MySQL MHA 部署及运行状态信息的功能实现
    Linux常用命令总结(二)
    python 学习笔记 (四)
    MySQL 学习笔记(四)
    学习ProxySQL参考到几个网址
  • 原文地址:https://www.cnblogs.com/autoint/p/13150352.html
Copyright © 2011-2022 走看看