ybtoj Au 数据结构优化 DP
前言中的前言
-
由于本人过菜,有些题解会咕掉,请原谅这个蒟蒻
-
由于本人过菜,不知道什么时候就
了,想给这个机房留下点什么…… -
如果想看高效进阶的题解,建议出门左拐,去云落那里看看,保证是全网最全最好的,但不要对云落的博客好奇,更不要看云落的一言 和 云落的合集:黑夜刀己,白日爱人
-
如果解决了题解里本蒟蒻的问题,将会获得任意奖励一份(不一定是物质哦),就比如这篇题解就有一个问题
正片开始!
好耶!!!(泪目)
题面
前言
数据结构优化 dp T1,水题
正文
设
转移
显然
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=10005;
const int mod=123456789;
int n,m;
int lowbit(int x){return x&-x;}
int c[N][105],a[N],b[N];
void update(int x,int d,int k){
for(;x<=N-5;x+=lowbit(x)){
c[x][k]=(c[x][k]+d)%mod;
}
}
int query(int x,int k){
int res=0;
for(;x;x-=lowbit(x)){
res=(res+c[x][k])%mod;
}
return res;
}
signed main(){
while(cin>>n>>m){
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++){
cin>>a[i];
b[i]=a[i];
}
sort(b+1,b+1+n);
int size=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+1+size,a[i])-b;
}
for(int i=1;i<=n;i++){
update(a[i],1,1);
for(int j=2;j<=m;j++){
int tmp=0;
tmp=query(a[i]-1,j-1);
if(!tmp)break;
update(a[i],tmp,j);
}
}
cout<<query(N-5,m)%mod<<endl;
}
return 0;
}
后记
云落秒了
题面
前言
数据结构优化 dp T2,应用题
正文
其实这题直接看题解就好了(看不懂就多看几遍)
记
转移:
滚动一下
问题是快速的求
对于任意一个村庄
这样当我们推导
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e4+6;
const int inf=1e18;
int ls(int p){return p<<1;}
int rs(int p){return p<<1|1;}
int n,k,d[N],c[N],s[N],w[N],st[N],ed[N],f[N],tree[N<<2],tag[N<<2];
struct node{int to,nxt;}e[N];
int head[N],cnt;
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void pushup(int p){tree[p]=min(tree[ls(p)],tree[rs(p)]);}
void build(int p,int pl,int pr){
tree[p]=0,tag[p]=0;
if(pl==pr){
tree[p]=f[pl];
return;
}
int mid=(pl+pr)>>1;
build(ls(p),pl,mid);
build(rs(p),mid+1,pr);
pushup(p);
//cout<<tree[p]<<" ";
}
void addtag(int p,int d){
tree[p]+=d;
tag[p]+=d;
}
void pushdown(int p){
if(tag[p]){
addtag(ls(p),tag[p]);
addtag(rs(p),tag[p]);
tag[p]=0;
}
}
void update(int p,int pl,int pr,int L,int R,int d){
if(L>R)return;
if(pl>=L&&pr<=R){
addtag(p,d);
return;
}
pushdown(p);
int mid=(pl+pr)>>1;
//cout<<mid<<" ";
if(L<=mid)update(ls(p),pl,mid,L,R,d);
if(R>mid)update(rs(p),mid+1,pr,L,R,d);
pushup(p);
}
int query(int p,int pl,int pr,int L,int R){
if(L>R)return inf;
if(pl>=L&&pr<=R){
return tree[p];
}
pushdown(p);
int mid=(pl+pr)>>1,res=inf;
if(L<=mid)res=min(res,query(ls(p),pl,mid,L,R));
if(R>mid)res=min(res,query(rs(p),mid+1,pr,L,R));
return res;
}
signed main(){
cin>>n>>k;
for(int i=2;i<=n;i++)cin>>d[i];
for(int i=1;i<=n;i++)cin>>c[i];
for(int i=1;i<=n;i++)cin>>s[i];
for(int i=1;i<=n;i++)cin>>w[i];
n++,k++;
d[n]=inf,w[n]=inf;
for(int i=1;i<=n;i++){
st[i]=lower_bound(d+1,d+1+n,d[i]-s[i])-d;
ed[i]=upper_bound(d+1,d+1+n,d[i]+s[i])-d-1;
add(ed[i],i);
//cout<<st[i]<<" "<<ed[i]<<endl;
}
int now=0;
for(int i=1;i<=n;i++){
f[i]=now+c[i];
for(int j=head[i];j;j=e[j].nxt){
int v=e[j].to;
now+=w[v];
}
//cout<<"*"<<f[i]<<endl;
}
int ans=inf;
for(int j=2;j<=k;j++){
build(1,1,n);
// cout<<endl<<j<<endl;
for(int i=1;i<=n;i++){
f[i]=query(1,1,n,1,i-1)+c[i];
for(int k=head[i];k;k=e[k].nxt){
int v=e[k].to;
update(1,1,n,1,st[v]-1,w[v]);
}
//cout<<f[i]<<" ";
}
ans=min(f[n],ans);
}
cout<<ans;
return 0;
}
后记
T2 就紫了?
题面
前言
数据结构优化 dp T3,水题
正文
本蒟蒻原先写了一个 4 个树状数组的写法,然后调不出来……
转移:
这个直接优化即可(不要像本蒟蒻一样写 4 个……)
代码
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+4;
const int mod=1e5+7;
int n,k;
struct node{int x,y;}e[N];
bool cmp(node a,node b){return a.x<b.x;}
int dp[N][12][2],tree[N][12][2];
int lowbit(int x){return x&-x;}
void add(int x,int k,int o,int d){
for(;x<=N;x+=lowbit(x))tree[x][k][o]=(d+tree[x][k][o])%mod;
}
int query(int x,int k,int o){
int res=0;
for(;x;x-=lowbit(x))res=(res+tree[x][k][o])%mod;
return res;
}
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>e[i].x>>e[i].y;
}
sort(e+1,e+n+1,cmp);
for(int i=1;i<=n;i++){
dp[i][0][0]=dp[i][0][1]=1;
add(e[i].y,0,0,1);
add(e[i].y,0,1,1);
for(int j=1;j<=k;j++){
dp[i][j][0]=(query(N,j,0)-query(e[i].y,j,0)+mod)%mod+(query(N,j-1,1)-query(e[i].y,j-1,1)+mod)%mod;
dp[i][j][1]=(query(e[i].y-1,j,1)+query(e[i].y-1,j-1,0))%mod;
add(e[i].y,j,0,dp[i][j][0]);
add(e[i].y,j,1,dp[i][j][1]);
}
}
int kazuha=0;
for(int i=1;i<=n;i++)kazuha=(kazuha+dp[i][k][0]+dp[i][k][1])%mod;
cout<<kazuha;
return 0;
}
后记
被教练叫去写
题面
前言
数据结构优化 dp T4,应用题
正文
把限制展开得
会发现满足其中一个式子,另一个也满足
不妨让两个都满足
将一个式子排序,另一个用树状数组维护
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
#define int long long
int w,n,b[N];
struct node{int l,r,v;}e[N];
bool cmp(node a,node b){
return a.l<b.l;
}
int c[N];
int lowbit(int x){return x&-x;}
void add(int x,int d){
for(;x<=N;x+=lowbit(x))c[x]=max(d,c[x]);
}
int query(int x){
int res=0;
for(;x;x-=lowbit(x))res=max(res,c[x]);
return res;
}
signed main(){
cin>>w>>n;
for(int i=1;i<=n;i++){
int t,p;
cin>>t>>p>>e[i].v;
e[i].l=2*t-p,e[i].r=2*t+p;
b[i]=e[i].r;
}
sort(b+1,b+1+n);
int op=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++){
e[i].r=lower_bound(b+1,b+1+op,e[i].r)-b;
}
sort(e+1,e+1+n,cmp);
for(int i=1;i<=n;i++){
int res=query(e[i].r);
add(e[i].r,res+e[i].v);
}
cout<<query(op);
return 0;
}
后记
貌似不用回家了
题面
前言
数据结构优化 dp T5,应用题
本蒟蒻不会单
正文
设
然后树状数组维护
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+4;
int n,k,h[N],X,Y,ans,c[505][N];
int lowbit(int x){return x&-x;}
// void update(int x,int y,int d){
// cout<<x<<" "<<y<<" "<<d<<" "<<X<<" "<<Y<<" "<<endl;
// for(;x<=X;x+=lowbit(x)){
// for(;y<=Y;y+=lowbit(y)){
// c[x][y]=max(c[x][y],d);
// cout<<lowbit(x)<<" "<<y<<endl;
// }
// }
// }
void update(int x,int y,int d) {
//cout<<x<<" "<<y<<" "<<val<<endl;
for (int i = x; i <= X; i += lowbit(i)) {
for (int j = y; j <= Y; j += lowbit(j)) {
c[i][j]=max(c[i][j],d);
//cout<<i<<" "<<j<<endl;
}
}
}
// int query(int x,int y){
// int res=0;
// for(;x;x-=lowbit(x)){
// for(;y;y-=lowbit(y)){
// res=max(c[x][y],res);
// }
// }
// return res;
// }
int query(int x, int y) {
int ret = 0;
for (int i = x; i; i -= lowbit(i)) {
for (int j = y; j; j -= lowbit(j)) {
ret=max(ret, c[i][j]);
}
}
return ret;
}
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>h[i];
Y=max(Y,h[i]);
}
X=k+1,Y+=k;//cout<<X<<" "<<Y;
for(int i=1;i<=n;i++){
for(int j=k;~j;j--){
int x=query(j+1,h[i]+j)+1;
//cout<<x<<endl;
ans=max(ans,x);
update(j+1,h[i]+j,x);
}
}
cout<<ans;
return 0;
}
后记
问题来咯
为什么代码中注释的写法不行,本蒟蒻觉得是等价的啊
题面
前言
数据结构优化 dp T6,不用数据结构优化题,人类智慧题
正文
有组合数做法捏
先讲正解
如题解所言,有三个性质
-
在一个波动数列中,若
与 不相邻,那么我们直接交换这两个数字就可以组成一个新的波动数列 -
把波动数列中的每个数字 i 变成 (n+1)-i 会得到另一个波动数列,且新数列的山峰与山谷情况相反
-
波动序列有对称性(就是把序列颠倒过来)
设
由于性质 2,答案要
相邻时,此时问题转化成了求 i-1个数,j-1为头,但是j-1 为山谷的方案数,由性质2可知,j-1作山谷和 (i-1)-(j-1)+1 山峰的方案数相同;
现在的问题就是,此时的区间和我DP方程的区间意义不同;
山峰与山谷是相对位置关系,
将 [j+1,i] 区间的每个数都减 1,这样是不改变相对大小关系的,并且此时就符合我们的方程了;
所以
滚动一下!!
只有10行的代码
#include <bits/stdc++.h>
using namespace std;
long long n,p,dp[2][4205],kazuha;
int main(){
cin>>n>>p;
dp[0][2]=1;
for(int i=3;i<=n;i++)for(int j=2;j<=i;j++)dp[i&1][j]=(dp[i&1][j-1]+dp[(i-1)&1][i-j+1])%p;
for(int j=2;j<=n;j++)kazuha=(kazuha+dp[n&1][j])%p;
cout<<kazuha*2%p;
}
组合数!!!
考虑把两端用最大的数分开(恐狼?)
考虑加上山峰山谷的限制
最大的数放在偶数位后时,它前面的一段山峰数为偶数,又因为最大的数前的一定是山谷,所以前面的山脉的开头就一定是山峰,后面的山脉开头显然是山谷
if(j%2==0)f[i][1]+=f[j][1]*f[i-1-j][0]*C[i-1][j];
if(j&1)f[i][0]+=f[j][0]*f[i-1-j][0]*C[i-1][j];
但是会发现山峰山谷的答案其实是一样的(性质2)
那么就可以把第二维省去了
滚动一下组合数
懒得压了
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=4205;
int n,p,f[N],c[2][N];
signed main(){
ios::sync_with_stdio(false);
cin>>n>>p;
f[0]=1;f[1]=1;
c[0][0]=1;
c[1][0]=1;c[1][1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++)c[i&1][j]=(c[i&1^1][j-1]+c[i&1^1][j])%p;
for(int j=0;j<=i-1;j++){
if(j&1){
f[i]=(f[i]+c[i&1^1][j]*f[j]%p*f[i-1-j])%p;
}
}
}
cout<<f[n]*2%p<<endl;
return 0;
}
后记
好了好了,去写
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】