[LuoguP6144] [USACO20FEB]ヘルプ自分P(DP + +組合せ論セグメントツリー)

[LuoguP6144] [USACO20FEB]ヘルプ自分P(DP + +組合せ論セグメントツリー)

フェイス質問

ベッシーはN内のセグメントの数は、軸線があり、i番目のセグメントカバー\([L_iを、R_iを( 1 \当量L_iを、R_iを\当量2N)\) すべての実数の。すべての実数のセグメントの集合を定義し、少なくとも一つのセグメントが覆われています。Kのセットとパワーブロックの数ユニコムのセグメント収集の複雑さを規定します。
ベッシーは今Nの線分計算する\(2 ^ N \)のサブセットと金型の複雑さを\(9 + 10 ^ 7 \)

分析

最初の行の順序。
セット\(dp_ {I、J、 T} \) 前方を表す\(I \)の線分は、右端点に覆わ\(J \)すべてのサブセットは、各サブセットの通信ブロックの数\ (T \)乗の和。(t番目のパワーに問題プレーンの複雑さに相当する理由は、次の二項定理の転送を使用することである)。
明らかに、初期値\(dp_ = {0,0,0}。1 \) 答えは\(\ sum_ {J = 1 } ^ {2N} dp_ {N、J、K} \)

最初の追加を検討(I \)\線分\([L_iを、R_iを] \ ) 回答に影響を与えます。

(1)(J <L_iを\)\状態、加え\([L_iを、R_iを] \ ) ブロック番号の後には、右端点となる+1通信となる(\ R_iを)\。提供\(dp_ { I-1、J、T} \) のサブセットに対応する\(S \) \(CNT(S)\)接続ブロックのサブセットの数。\(dp_ {I、R_iを、 tは} \) の値を増加させます

\ [\ sum_ {S}(CNT(S)+1)^ T = \ sum_ {S} \ sum_ {I = 1} ^ K C_ {T} ^ I CNT(S)^ T = \ sum_ {S} \ sum_は{I = 1} ^ K C_ {T} ^ I dp_ {I-1、J、T} \]

(2)(L_iを\当量J \当量R_iを \)\ 状態、加え\([L_iを、R_iを] \ ) 接続ブロックの同じ番号の後、右端点となる\(R_iを\) (\ dp_ {I、R_iを、T} \) の値を増加させるであろう\(dp_ {I-1、 J、T} \)

(3)について(J> R_iを\)\状態、加え\([L_iを、R_iを] \ ) 同じ通信のブロック番号の後、右端の点は変わりません。しかしながら、サブセットによって2の数(各サブセットは、選択またはセクション選択されていないことができる\(I \)間隔)。各サブセット通信におけるブロックの数は変わらないので、それほど(dp_ {I、J、\ \ T}) の2による。

その後、我々は、ツリーラインとDP転送を維持することができます。明らかに\(I \)この寸法は、セグメントツリーのリーフノードを除去することができる([jは、j]は\ \ ) 維持\(K \)を表す次元ベクトル\(dp_ {I、Jを} \)
(1)シーク\([0、L-1 ] \) 間隔、次いで上記合計量に応じて増加を計算し、次に\(R&LT \)単一ポイント増加する。
見つける(2)\([L、R] \ )間隔と、その後\(R&LT \)単一ポイント増加する。
(3)間隔の\([R + 1、N ] \) 2により間隔
すべてのクエリ操作は、操作の前に修正することに留意されたいです。だから、サポートプラス、ベクトル、ベクトル乗算間隔の単一のポイントを維持するために、間隔のクエリベクトルとセグメントツリーをすることができます。

複雑\(O(NK \ログN )\)

コード

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 200000
#define maxk 10
#define mod 1000000007
using namespace std;
typedef long long ll;
int n,K;
struct seg{
	int l;
	int r;
	friend bool operator < (seg p,seg q){
		if(p.l==q.l) return p.r<q.r;
		else return p.l<q.l;
	}
}a[maxn+5];

ll C[maxk+5][maxk+5];
void ini(int m){
	for(int i=0;i<=m;i++){
		C[i][0]=C[i][i]=1;
		for(int j=0;j<i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	}
}
struct val_type{//维护一个K维向量 
	ll a[maxk+5];
	val_type(){
		memset(a,0,sizeof(a));
	}
	ll & operator [] (const int i){
		return a[i];
	}
	friend val_type operator + (val_type p,val_type q){
		val_type ans;
		for(int i=0;i<=K;i++) ans[i]=(p[i]+q[i])%mod;
		return ans;
	}
	friend val_type operator * (val_type p,ll x){
		val_type ans;
		for(int i=0;i<=K;i++) ans[i]=p[i]*x%mod;
		return ans;
	}
	void print(){
		printf("debug:");
		for(int i=0;i<=K;i++) printf("%lld ",a[i]);
		printf("\n");
	}
};

struct segment_tree{
	struct node{
		int l;
		int r;
		ll mtag;
		val_type v;
	}tree[maxn*4+5];
	void push_up(int pos){
		tree[pos].v=tree[pos<<1].v+tree[pos<<1|1].v;
	}
	void mul_tag(int pos,int v){
		tree[pos].mtag=tree[pos].mtag*v%mod;
		tree[pos].v=tree[pos].v*v; 
	}
	void push_down(int pos){
		if(tree[pos].mtag!=1){
			mul_tag(pos<<1,tree[pos].mtag);
			mul_tag(pos<<1|1,tree[pos].mtag);
			tree[pos].mtag=1;
		}
	}
	void build(int l,int r,int pos){
		tree[pos].l=l;
		tree[pos].r=r;
		tree[pos].mtag=1;
		if(l==r) return;
		int mid=(l+r)>>1;
		build(l,mid,pos<<1);
		build(mid+1,r,pos<<1|1); 
	} 
	void add_point(int upos,val_type &uval,int pos){//单点加向量 
		if(tree[pos].l==tree[pos].r){
			tree[pos].v=tree[pos].v+uval;
			return;
		}
		push_down(pos);
		int mid=(tree[pos].l+tree[pos].r)>>1;
		if(upos<=mid) add_point(upos,uval,pos<<1);
		else add_point(upos,uval,pos<<1|1);
		push_up(pos); 
	}
	void mul_seg(int L,int R,ll uval,int pos){//区间数乘 
		if(L<=tree[pos].l&&R>=tree[pos].r){
			mul_tag(pos,uval);
			return;
		}
		push_down(pos);
		int mid=(tree[pos].l+tree[pos].r)>>1;
		if(L<=mid) mul_seg(L,R,uval,pos<<1);
		if(R>mid) mul_seg(L,R,uval,pos<<1|1);
		push_up(pos);
	} 
	val_type query(int L,int R,int pos){//查询区间向量和 
		if(L<=tree[pos].l&&R>=tree[pos].r){
			return tree[pos].v;
		}
		push_down(pos);
		int mid=(tree[pos].l+tree[pos].r)>>1;
		val_type ans;
		if(L<=mid) ans=ans+query(L,R,pos<<1);
		if(R>mid) ans=ans+query(L,R,pos<<1|1);
		return ans;
	}
}T; 
int main(){
	scanf("%d %d",&n,&K);
	ini(K);
	for(int i=1;i<=n;i++) scanf("%d %d",&a[i].l,&a[i].r);
	sort(a+1,a+1+n);
	T.build(0,n*2,1);
	val_type tmp;
	tmp[0]=1; 
	T.add_point(0,tmp,1);//dp[0]初始化为1
	for(int i=1;i<=n;i++){
		int l=a[i].l,r=a[i].r;
		val_type last=T.query(0,l-1,1);
//		last.print();
		val_type now;
		for(int i=0;i<=K;i++){
			for(int j=0;j<=i;j++){
				now[i]+=last[j]*C[i][j]%mod; 
				now[i]%=mod;
			}
		}
		now=now+T.query(l,r,1);
//		now.print();
		T.add_point(r,now,1);
		if(r!=n*2) T.mul_seg(r+1,n*2,2,1);
//		T.tree[1].v.print();
	} 
	printf("%lld\n",T.tree[1].v[K]);
}

おすすめ

転載: www.cnblogs.com/birchtree/p/12555498.html