1. 程式人生 > 其它 >「BZOJ3688」折線統計 題解

「BZOJ3688」折線統計 題解

熬煞我的97行程式碼

DP分析

· 狀態:使用三元組 \(f(i,j,false/true)\) 表示在前 \(i\) 個點中,選取了 \(j\) 條線段,其中最後一條線段是下降 \((false)\) 或上升 \((true)\) 的方案數;
· 目標:$$f(n,k,false)+f(n,k,true)$$
· 狀態轉移方程

\[ \left\{ \begin{aligned} f(i,j,false) & = \Sigma\{ f(k,j,false), f(k,j-1,true) \} & (k<i , a[k].y>a[i].y)\\ \\ f(i,j,true) & = \Sigma\{ f(k,j,true), f(k,j-1,false) \} & (k<i , a[k].y>a[i].y) \end{aligned} \right. \]

· 當然逃不過樹狀陣列優化

具體實現

\(a\) 陣列沿X軸排序,並對Y軸進行離散化。

struct XOY{
	int x,y;
	friend bool operator<(XOY fir,XOY sec){
		return fir.x<sec.x;
	}
}a[maxn];

出於未知的幻想,鄙人先使用了二叉搜尋樹對 \(a.y\) 離散化:

struct BSTree{
	struct BSTree_node{
		int num;
		bool vis;
		int l,r;
	}b[maxn];
	int tot,a_tot;
	inline void f(){
		tot=1;//最容易忘掉的頭疼東西
		a_tot=0;
	}
	inline void push(int k,int x){//存樹
		if(!b[k].vis){
			b[k].num=x;
			b[k].vis=true;
			return ;
		}else if(x<=b[k].num){//大的相等的存左兒子
			if(!b[k].l)b[k].l=++tot;//沒兒子,新增節點,到最後b的順序會驚喜地等於a.y的順序
			push(b[k].l,x);
		}else if(x>b[k].num){//小的存右兒子
			if(!b[k].r)b[k].r=++tot;
			push(b[k].r,x);
		}
	}
	inline void disc(int k){//離散
		if(!b[k].vis)return ;
		disc(b[k].l);
		a[k].y=++a_tot;
		disc(b[k].r);
	}
}bst;

main:

bst.f();
for(int i=1;i<=n;i++){
	a[i].x=read();a[i].y=read();
	bst.push(1,a[i].y);
}
bst.disc(1);
sort(a+1,a+1+n);

使用樹狀陣列 \(t(j,false/true)\) 省略第一維:

struct BITree{
	int c[maxn];
	inline int lowbit(int x){return x&(-x);}
	inline void add(int x,int val){
		val=(val%mod+mod)%mod;//這個特別重要,下文解釋
		while(x<=n){
			c[x]=(c[x]+val)%mod;
			x+=lowbit(x);
		}
	}
	inline int query(int x){
		int ret=0;
		while(x){
			ret=(c[x]+ret)%mod;
			x-=lowbit(x);
		}
		return ret;
	}
}t[11][2];

main:

for(int i=1;i<=n;i++){
	t[0][0].add(a[i].y,1);
	t[0][1].add(a[i].y,1);
	for(int j=1;j<=k;j++){
		int tmpa=t[j][0].query(n)-t[j][0].query(a[i].y);
		int tmpb=t[j-1][1].query(n)-t[j-1][1].query(a[i].y);
		int tmpc=t[j][1].query(a[i].y-1)+t[j-1][0].query(a[i].y-1);
		t[j][0].add(a[i].y,tmpa+tmpb);
		t[j][1].add(a[i].y,tmpc);
	}
}

有一點差點把我卡惑了:

val=(val%mod+mod)%mod;

這個語句在 BITree.add(int x,int val) 中,需要注意的是,用 tmpa+tmpb 傳導的 val 中有減法,而 tmpatmpb 獲取的值本身是進行過 mod ,所以不可以直接 val%=mod

舉個例子:

比如 x=21 , y=19 , mod=5
那麼 val 應該為 (21-19)% 5 = 2;
而事實上,x在 BITree 中取模後值為 1,y值為 4
此時 val 應該為 (1-4+5)% 5 = 2;而不是(1-4)% 5 = 2;

完整 AC Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=50010;
const int mod=1e5+7;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
int n,k;
struct BITree{
	int c[maxn];
	inline int lowbit(int x){return x&(-x);}
	inline void add(int x,int val){
		val=(val%mod+mod)%mod;
		while(x<=n){
			c[x]=(c[x]+val)%mod;
			x+=lowbit(x);
		}
	}
	inline int query(int x){
		int ret=0;
		while(x){
			ret=(c[x]+ret)%mod;
			x-=lowbit(x);
		}
		return ret;
	}
}t[11][2];
struct XOY{
	int x,y;
	friend bool operator<(XOY fir,XOY sec){
		return fir.x<sec.x;
	}
}a[maxn];
struct BSTree{
	struct BSTree_node{
		int num;
		bool vis;
		int l,r;
	}b[maxn];
	int tot,a_tot;
	inline void f(){
		tot=1;
		a_tot=0;
	}
	inline void push(int k,int x){
		if(!b[k].vis){
			b[k].num=x;
			b[k].vis=true;
			return ;
		}else if(x<=b[k].num){
			if(!b[k].l)b[k].l=++tot;
			push(b[k].l,x);
		}else if(x>b[k].num){
			if(!b[k].r)b[k].r=++tot;
			push(b[k].r,x);
		}
	}
	inline void disc(int k){
		if(!b[k].vis)return ;
		disc(b[k].l);
		a[k].y=++a_tot;
		disc(b[k].r);
	}
}bst;
int main(){
	n=read();k=read();
	bst.f();
	for(int i=1;i<=n;i++){
		a[i].x=read();a[i].y=read();
		bst.push(1,a[i].y);
	}
	bst.disc(1);
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++){
		t[0][0].add(a[i].y,1);
		t[0][1].add(a[i].y,1);
		for(int j=1;j<=k;j++){
			int tmpa=t[j][0].query(n)-t[j][0].query(a[i].y);
			int tmpb=t[j-1][1].query(n)-t[j-1][1].query(a[i].y);
			int tmpc=t[j][1].query(a[i].y-1)+t[j-1][0].query(a[i].y-1);
			t[j][0].add(a[i].y,tmpa+tmpb);
			t[j][1].add(a[i].y,tmpc);
		}
	}
	printf("%d\n",(t[k][0].query(n)+t[k][1].query(n))%mod);
	return 0;
} 

97行萬歲

$$-----END------$$