[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は} \) の値を増加させます
(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]);
}