「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 中有减法,而 tmpa 和 tmpb 获取的值本身是进行过 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行万岁

浙公网安备 33010602011771号