二维积水(DP优化)
题面
在二向箔爆发前的时间里,宇宙中就有一个叫地球的星球,上面存在过奴隶主,后来绝迹了……
——《第三维的往事》
在这个美丽的二维宇宙中,有一个行星叫地圆。地圆有一条大陆叫美洲,上面生活着一个奴隶主,叫
S
Y
SY
SY ,经营着数不清的☻奴隶在一条长为
N
m
N~\tt m
N m 的种植园上劳作。
奴隶们苦不堪言,因为这条种植园地势崎岖。具体地说,从左到右第
i
i
i 块长为
1
m
1~\tt m
1 m 的地高度为(正整数)
h
i
m
h_i ~\tt m
hi m ,并且种植园左边和右边的广阔荒地出奇的平坦和低洼,它们的高度都是
0
0
0 。
另外,奴隶们还很怕下雨,因为雨只会在种植园上空下,一旦下雨,就会积水(水面线是平的,积水原则和实际相符)。而且每次的雨都足够大,会下到积水量不再增加为止。
// Sample #1, widened
..............
..........##..
..##~~~~~~##..
..##~~##~~##..
..##~~##~~##..
########~~####
##############
S
Y
SY
SY 在每次下雨后都会统计一次棉花产出,他发现,如果雨停后残留的积水总量(单位:
m
2
\tt m^2
m2)为偶数,那么棉花的产出会非常高。
于是
S
Y
SY
SY 决定发动奴隶们铲地,把刚好
K
K
K 块长为
1
m
1~\tt m
1 m 的地高度铲为
0
0
0 ,使得下一次来雨后,积水的总量为偶数。
由于二维宇宙的电子计算机太慢了,于是他把数据发给了三维宇宙的你,询问总共
(
N
K
)
{N\choose K}
(KN) 种方案中,有多少种铲地方案可以达到他的要求。答案对
1
0
9
+
7
\tt 10^9+7
109+7 取模。
输入
第一行两个整数
N
,
K
N,K
N,K 。
第二行一个长为
N
N
N 的正整数数列
h
h
h 。
每行的数字之间以空格分隔。
1
≤
N
≤
25000
,
1
≤
h
i
≤
1
0
9
,
1
≤
K
≤
min
(
25
,
N
−
1
)
1\leq N\leq25000,1\leq h_i\leq 10^9,1\leq K\leq \min(25,N-1)
1≤N≤25000,1≤hi≤109,1≤K≤min(25,N−1).
输出
一个取模后的整数表示答案。
样例
Input #1
7 1
2 5 2 4 1 6 2
Output #1
4
Input #2
18 8
6 3 2 2 2 1 5 1 2 2 5 5 5 4 6 4 4 1
Output #2
21931
题解
我们设简单一点的
D
P
DP
DP 状态,令
d
p
l
[
i
]
[
j
]
dpl[i][j]
dpl[i][j] 为前
i
−
1
i-1
i−1 块地中选了
j
j
j 块地铲掉,第
i
i
i 块地在保留地中最高(允许前面有和
i
i
i 等高的保留地) 的奇/偶方案数。没错,他是个二元组
(
奇
方
案
,
偶
方
案
)
(奇方案,偶方案)
(奇方案,偶方案)。这种二元组的乘法定义为
(
a
,
b
)
×
(
c
,
d
)
=
(
a
c
+
b
d
,
a
d
+
b
c
)
(a,b)\times(c,d)=(ac+bd,ad+bc)
(a,b)×(c,d)=(ac+bd,ad+bc) ,即两个不相干的地区的方案合并。定义加法为
(
a
,
b
)
+
(
c
,
d
)
=
(
a
+
c
,
b
+
d
)
(a,b)+(c,d)=(a+c,b+d)
(a,b)+(c,d)=(a+c,b+d) ,即同一性质的方案数累加。接下来就好做了。
转移比较恶心,感性理解吧:枚举
i
i
i 前面第二高的地
y
y
y (可以与
i
i
i 等高),从
d
p
l
[
y
]
[
j
−
c
n
t
]
dpl[y][j-cnt]
dpl[y][j−cnt] 转移到
d
p
l
[
i
]
[
j
]
dpl[i][j]
dpl[i][j] ,其中
c
n
t
cnt
cnt 为区间
(
y
,
i
)
(y,i)
(y,i) 内高度大于等于
h
y
h_y
hy 的土地个数,这些土地都是必须要铲的。再算上区间
(
y
,
i
)
(y,i)
(y,i) 内自由地的铲除方案,合在
d
p
l
[
y
]
[
i
−
c
n
t
]
dpl[y][i-cnt]
dpl[y][i−cnt] 里面一并转移过来。把
i
i
i 算好后,动态维护前面土地的转移数组,若
h
y
>
h
i
h_y>h_i
hy>hi,那么接下来的计算中,
i
i
i 都算作
y
y
y 转移时的自由地,于是把
i
i
i 铲与不铲的方案算进
d
p
l
[
y
]
dpl[y]
dpl[y] 中作为“转移用
d
p
l
[
y
]
dpl[y]
dpl[y]”,后面的土地再计算好后,同样地看情况算进“转移用
d
p
l
dpl
dpl”数组中。我们会发现,每时每刻可以用来转移的
y
y
y 最多只有
K
K
K 个左右,往往是最高的
K
K
K 个(相等的就是最靠右的优先)。因此复杂度
O
(
N
K
2
)
O(NK^2)
O(NK2) 。
我们还得计算类似的
d
p
r
[
i
]
[
j
]
dpr[i][j]
dpr[i][j] ,表示在第
i
i
i 块地右边的地选
j
j
j 块铲掉,不过此时不允许后面有高度大于等于
i
i
i 的保留地。而且转移中的所有细节取等与否是相反的(详见上方标黑,转移地不可与
i
i
i 等高,大于等于变成大于,但是等高的转移地仍然是最靠右的优先)。
最后我们把每个
d
p
l
[
i
]
[
j
]
×
d
p
r
[
i
]
[
K
−
j
]
dpl[i][j]\times dpr[i][K-j]
dpl[i][j]×dpr[i][K−j] 都贡献给答案。这相当于枚举了铲地后最高的那块地(等高的地以右为尊)。
CODE
唉,算了,如果没看懂可以看代码或者自己想。
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 25005
#define LL long long
#define ULL unsigned long long
#define UI unsigned int
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define eps (1e-4)
#define MI map<LL,int>::iterator
#pragma GCC optimize(2)
int xchar() {
static const int SZ = 1000005;
static char ss[SZ];
static int pos = 0,len = 0;
if(pos == len) pos = 0,len = fread(ss,1,SZ,stdin);
if(pos == len) return -1;
return ss[pos ++];
}
LL xread() {
LL f=1,x=0;int s = xchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = xchar();}
while(s >= '0' && s <= '9') {x=x*10+(s^48);s = xchar();}
return f*x;
}
LL read() {
LL f=1,x=0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f*x;
}
void putpos(LL x) {
if(!x) return ;
putpos(x/10); putchar('0'+(x%10));
}
void putnum(LL x) {
if(!x) putchar('0');
else if(x < 0) putchar('-'),putpos(-x);
else putpos(x);
}
void AIput(LL x,char c) {putnum(x);putchar(c);}
const int MOD = 1000000007;
int n,m,s,o,k;
int h[MAXN];
struct it{
int n0,n1;
it(){n0=n1=0;}
it(int ev,int od){n0=ev;n1=od;}
};
it& operator += (it &a,it b) {
(a.n0 += b.n0) %= MOD;
(a.n1 += b.n1) %= MOD;
return a;
}
it operator + (it a,it b) {
(a.n0 += b.n0) %= MOD;
(a.n1 += b.n1) %= MOD;
return a;
}
it operator * (it a,it b) {
it c;c.n0 = (a.n1*1ll*b.n1%MOD + a.n0*1ll*b.n0%MOD)%MOD;
c.n1 = (a.n1*1ll*b.n0%MOD + a.n0*1ll*b.n1%MOD)%MOD;
return c;
}
set<pair<int,int> > st;
it dp[MAXN][30];
it fl[MAXN][30],fr[MAXN][30];
map<int,int> mp;
vector<int> bu[MAXN];
int nm;
struct tr{
int ls,rs;
LL nm; int ct;
tr(){ls=rs=nm=ct=0;}
}tre[MAXN*42];
int CNT = 0;
int addtree(int a,int x,int al,int ar,int y) {
if(al > x || ar < x) return a;
tre[++CNT] = tre[a]; a = CNT;
tre[a].nm += y; tre[a].ct ++;
if(al == ar) return a;
int md = (al + ar) >> 1;
tre[a].ls = addtree(tre[a].ls,x,al,md,y);
tre[a].rs = addtree(tre[a].rs,x,md+1,ar,y);
return a;
}
void gettree(int a,int l,int r,int al,int ar,int &c,LL &y) {
if(l > r || al > r || ar < l || !a) return ;
if(al >= l && ar <= r) {c += tre[a].ct,y += tre[a].nm;return ;}
int md = (al + ar) >> 1;
gettree(tre[a].ls,l,r,al,md,c,y);
gettree(tre[a].rs,l,r,md+1,ar,c,y);
return ;
}
int rt[MAXN];
LL su[MAXN];
int cnc[MAXN];
int main() {
freopen("rain.in","r",stdin);
freopen("rain.out","w",stdout);
n = read();m = read();
for(int i = 1;i <= n;i ++) {
h[i] = read();
su[i] = su[i-1] + h[i];
mp[h[i]] = 1;
}
for(auto i = mp.begin();i != mp.end();i ++) {i->SE = ++ nm;}
for(int i = 1;i <= n;i ++) bu[mp[h[i]]].push_back(i);
for(int i = nm;i > 0;i --) {
rt[i] = rt[i+1];
for(int j = 0;j < (int)bu[i].size();j ++) {
rt[i] = addtree(rt[i],bu[i][j],1,n,h[bu[i][j]]);
}
}
rt[0] = rt[1];
st.insert(make_pair(0,0));
dp[0][0].n0 = 1;
for(int i = 1;i <= n;i ++) {
int cn = 0;
for(auto j = st.begin();j != st.end() && cn <= m;j ++) {
int y = -j->SE;
if(h[y] > h[i]) {cn ++;continue;}
int cc = 0; LL qu = 0;
qu = 0;
gettree(rt[mp[h[y]]],y+1,i-1,1,n,cc,qu);
cn = cnc[y];
if(cn > m) break;
LL sm = h[y]*1ll*(i-y-1) - (su[i-1]-su[y]-qu);
for(int k = cc;k <= m;k ++) {
it tm = dp[y][k - cc];
if(sm & 1) swap(tm.n0,tm.n1);
dp[i][k] += tm;
}
}
memcpy(fl[i],dp[i],sizeof(fl[i]));
cn = 0;
it my = it(h[i]%2 == 0,h[i]%2);
for(auto j = st.begin();j != st.end() && cn <= m;j ++,cn ++) {
int y = -j->SE;
if(cnc[y] > m) break;
if(h[y] > h[i]) {
for(int k = m;k > 0;k --) {
dp[y][k] += dp[y][k-1]*my;
}
cnc[i] ++;
}
else cnc[y] ++;
}
st.insert(make_pair(-h[i],-i));
}
st.clear();
st.insert(make_pair(0,-n-1));
dp[n+1][0].n0 = 1;
cnc[n+1] = 0;
for(int i = 1;i <= n;i ++) {
cnc[i] = 0;
for(int j = 0;j <= m;j ++) dp[i][j] = it();
}
for(int i = n;i > 0;i --) {
int cn = 0;
for(auto j = st.begin();j != st.end() && cn <= m;j ++) {
int y = -j->SE;
if(h[y] >= h[i]) {cn ++;continue;}
int cc = 0; LL qu = 0;
gettree(rt[mp[h[y]]+1],i+1,y-1,1,n,cc,qu);
cn = cnc[y];
if(cn > m) break;
LL sm = h[y]*1ll*(y-i-1) - (su[y-1]-su[i]-qu);
for(int k = cc;k <= m;k ++) {
it tm = dp[y][k - cc];
if(sm & 1) swap(tm.n0,tm.n1);
dp[i][k] += tm;
}
}
memcpy(fr[i],dp[i],sizeof(fr[i]));
cn = 0;
it my = it(h[i]%2 == 0,h[i]%2);
for(auto j = st.begin();j != st.end() && cn <= m;j ++) {
int y = -j->SE;
if(cnc[y] > m) break;
if(h[y] >= h[i]) {
for(int k = m;k > 0;k --) {
dp[y][k] += dp[y][k-1]*my;
}
cnc[i] ++;
}
else cnc[y] ++;
}
st.insert(make_pair(-h[i],-i));
}
int ans = 0;
for(int i = 1;i <= n;i ++) {
for(int j = 0;j <= m;j ++) {
it nw = fl[i][j] * fr[i][m-j];
(ans += nw.n0) %= MOD;
}
}
if(m == n) (ans += 1) %= MOD;
AIput(ans,'\n');
return 0;
}
二维积水(DP优化)的更多相关文章
- (转载)Android项目实战(二十八):使用Zxing实现二维码及优化实例
Android项目实战(二十八):使用Zxing实现二维码及优化实例 作者:听着music睡 字体:[增加 减小] 类型:转载 时间:2016-11-21我要评论 这篇文章主要介绍了Android项目 ...
- Firemonkey 原生二维码扫描优化
之前用了ZXing的Delphi版本,运行自带的例子,速度非常慢,与安卓版本的相比查了很多,因此打算使用集成jar的方法,但是总觉得美中不足. 经过一番研究,基本上解决了问题. 主要有两方面的优化: ...
- 01二维背包+bitset优化——hdu5890
口胡一种别的解法: 三重退背包,g1[j]k]表示不选x的选了j件物品,体积为k的方案数,g[0][0] = 1 , g1[j][k]=dp[j][k]-g1[j-1][k-a[x]] 然后按这样再退 ...
- POJ1722二维spfa+优先队列优化
题意: 给你一个有向图,然后求从起点到终点的最短,但是还有一个限制,就是总花费不能超过k,也就是说每条边上有两个权值,一个是长度,一个是花费,求满足花费的最短长度. 思路: 一开 ...
- 洛谷 P5471 - [NOI2019] 弹跳(二维线段树优化建图+堆优化存边)
题面传送门 一道非常有意思的题(大概可以这么形容?) 首先看到这类一个点想一个区域内连边的题目可以很自然地想到线段树优化建图,只不过这道题是二维的,因此需要使用二维线段树优化建图,具体来说,我们外层开 ...
- Vijos1392拼拼图的小衫[背包DP|二维信息DP]
背景 小杉的幻想来到了经典日剧<死亡拼图>的场景里……被歹徒威胁,他正在寻找拼图(-.-干嘛幻想这么郁闷的场景……). 突然广播又响了起来,歹徒竟然又有了新的指示. 小杉身为新一代的汤浅, ...
- UVA1347 旅游(二维递归DP)
旅游 [题目链接]旅游 [题目类型]DP &题解: 紫书P269 代码很简单,但思路很难.很难能想到要把一个圈分成2条线段,很难想到d(i,j)表示的是已经走过max(i,j)还需要的距离值, ...
- 棋盘分割(二维区间DP)
题目大意:给一个棋盘,棋盘上每个格子中都有一个值,现在需要将棋盘切成n个矩形,总共切n-1刀,求最小的均方差.均方差定义为:,其中. 题目分析:将均方差化简得到:均方差2=(Σxi2)/n-平均值2. ...
- POJ 1661 Help Jimmy(二维DP)
题目链接:http://poj.org/problem?id=1661 题目大意: 如图包括多个长度和高度各不相同的平台.地面是最低的平台,高度为零,长度无限. Jimmy老鼠在时刻0从高于所有平台的 ...
随机推荐
- 使用VMware安装Ubuntu虚拟机
一.下载安装VM软件 这一步跳过,因为网上都能找到下载地址,下载后一步一步的安装即可,网上也有很多下载地址,这里提供一个Windows的下载链接. 链接: https://pan.baidu.com/ ...
- hyperlpr centos 使用记录
1.下载最新版python3.7 Anacondawget https://repo.anaconda.com/archive/Anaconda3-5.3.1-Linux-x86_64.sh 2.安装 ...
- Java学习-第一部分-第一阶段-第二节:变量
变量 变量介绍 为什么需要变量 变量是程序的基本组成单位 不论是使用哪种高级程序语言编写程序,变量都是其程序的基本组成单位,比如: //变量有三个基本要素(类型+名称+值) class Test{ p ...
- powershell命令总结
2021-07-21 初稿 ps命令采用动词-名词的方式命名,不区分大小写.默认当前文件夹为当前路径./.除去-match使用正则表达式匹配外,其他都使用*和?通配符. 速查 管道命令 前一个的输出作 ...
- sap 调用Http 服务
REPORT ZMJ_GETAPI. DATA: LEN TYPE I, "发送报文长度 LEN_STRING TYPE STRING, URL TYPE STRING, "接口地 ...
- ngRoute 配置路径不能跳转问题
1.原因:AngularJS 版本更新至1.6后对地址做了特别处理.如:<a hret="#/someurl"> 在浏览器中被解析为"#!%2Fsomeurl ...
- RPA纳税申报机器人
1.机器人开始工作 2.机器人打开企业内部税务平台,自动下载报税底表 3.机器人自动登录地方税务局,填写报税数据 手工报税10分钟/3个表 VS 机器人报税时间2分钟/3个表 处理时间缩短80% 报税 ...
- Redis布隆过滤器和布谷鸟过滤器
一.过滤器使用场景:比如有如下几个需求:1.原本有10亿个号码,现在又来了10万个号码,要快速准确判断这10万个号码是否在10亿个号码库中? 解决办法一:将10亿个号码存入数据库中,进行数据库查询,准 ...
- HBuilderX配置外部服务器(tomcat)查看编辑jsp界面
HBuilderX配置外部服务器(tomcat)查看编辑jsp界面 一.第一种方法,通过启动本地tomcat,查看jsp 在tomcat的webapps目录下创建文件夹HBuilderX 打开HBui ...
- 【cartographer_ros】四: 发布和订阅里程计odom信息
上一节介绍了激光雷达Scan传感数据的订阅和发布. 本节会介绍里程计Odom数据的发布和订阅.里程计在cartographer中主要用于前端位置预估和后端优化. 官方文档: http://wiki.r ...