$vjudge-dp$专题题解
因为感觉题解写不了多少,,,就懒得一道道题目慢慢写了,汇总了算了$QAQ$
昂然后因为我估计以后还会有些什么$dp$专题啊$balabala$的,,,然后谢总肯定又会建一堆小组啥的,,,所以还是放个链接防止自己忘了题目都是啥$QAQ$
1:
$A$
先放下题目大意昂$QwQ$,就说给定$N$个人,$K$排,以及每排的人数$a_{1},...,a_{K}$,现要求每排人数递减,然后每列人数也递减,求排列方案数
显然考虑从矮到高考虑,就有一定要是满足轮廓是个凸的,就长得有点儿像之前寒假考试的搜索专题$D2\ T5$
这题就差不多套路,考虑五维$dp$:设$f_{d_1,d_2,d_3,d_4,d_5}$,然后判断$d_1\leqslant d_2$这种之类乱七八糟的条件,然后瞎转移一下,就欧克了
嗷对辣,就是,因为如果直接开$f_{30,30,30,30,30}$会炸空间,,,所以这里要对着读入开,,,啊呀说不清楚还是看$code$趴$QwQ$(,,,后来发现是我呆了,更优秀的开空间方式可以看神仙hl的博客$QwQ$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;
#define il inline
#define gc getchar()
#define int long long
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int M=+,N=1e7;
int n,as,a[M];
bool gdgs=; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:x;
} signed main()
{
while(gdgs)
{
ri n=read();if(!n)return ;rp(i,,n)a[i]=read();rp(i,n+,)a[i]=;
int f[a[]+][a[]+][a[]+][a[]+][a[]+];memset(f,,sizeof(f));f[][][][][]=;
rp(i,,a[])
{
rp(j,,min(a[],i))
{
rp(k,,min(a[],j))
{
rp(p,,min(a[],k))
{
rp(q,,min(a[],p))
{
if(q)f[i][j][k][p][q]+=f[i][j][k][p][q-];
if(p->=q)f[i][j][k][p][q]+=f[i][j][k][p-][q];
if(k->=p)f[i][j][k][p][q]+=f[i][j][k-][p][q];
if(j->=k)f[i][j][k][p][q]+=f[i][j-][k][p][q];
if(i->=j)f[i][j][k][p][q]+=f[i-][j][k][p][q];
}
}
}
}
}
printf("%lld\n",f[a[]][a[]][a[]][a[]][a[]]);
}
return ;
}
$B$
先翻译下$QAQ$?大概就是说有两个序列,然后求最长公共上升子序列
显然考虑$dp$呗,就设$f_{i,j}$:$a_{i}$和$b_{j}$配对的最长方案数
然后直接$O(T\cdot n^{2})$地做就好了鸭,具体来说就,把$a$作外层,$O(n^{2})$地扫,碰到两个情况要记录,第一个是$a_{i}=b_{j}$,显然考虑转移,问题就在于要找到在$j$之前的满足$b_{k}<a_{i}$的$f_{max}$.所以在碰到$a_{i}>b_{j}$的情况的时候就顺便记录下所有满足的$f_{max}$即可
至于输出方案就另开一个数组记录就好,,,挺水的懒得说了_(:з」∠)_
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define gc getchar()
#define int long long
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int M=+;
int n,a[M],f[M][M],b[M],pre[M][M]; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
} signed main()
{
// freopen("qwq.in","r",stdin);freopen("b.out","w",stdout);
ri T=read();
while(T--)
{
ri n=read();rp(i,,n)a[i]=read();ri m=read();rp(i,,m)b[i]=read();
memset(f,,sizeof(f));memset(pre,-,sizeof(pre));
rp(i,,n)
{
ri tmp_f=,tmp_j=;
rp(j,,m)
{
f[i][j]=f[i-][j];
if(a[i]>b[j] && tmp_f<f[i-][j])tmp_f=f[i-][j],tmp_j=j;
if(a[i]==b[j] && tmp_f+>f[i][j])f[i][j]=tmp_f+,pre[i][j]=tmp_j;
}
}
ri tmp_i=;rp(i,,m)if(f[n][i]>f[n][tmp_i])tmp_i=i;printf("%lld\n",f[n][tmp_i]);
if(!f[n][tmp_i])continue;
vector<int>as;my(i,n,)if(pre[i][tmp_i]!=-)as.push_back(a[i]),tmp_i=pre[i][tmp_i];printf("%lld",as[as.size()-]);
my(i,as.size()-,)printf(" %lld",as[i]);printf("\n");
}
return ;
}
$C$
这题长得有点儿像$HNOI2019\ D2T3$的10$pts$部分分那个,,,?
不过那题的数据范围是$1e3$,这题是$1e9$鸭$QAQ$
所以显然直接$dp$是不可取的,这里需要大胆猜结论发现,新的数列$b$一定都是原来的数列$a$中的数
瞎证下趴,,,$QAQ$
下面只尝试着证下在单调增的序列中,单调减同理即可$QwQ$
首先显然的是新的数列$b$中的每个数都一定在$[min_{a},max_{a}]$的范围内?
若最优解中$b_{i}$不在$a$中且属于$[a_{j},a_{j+1}]$,那它的下一个数的取值范围就在$[b_{i},+\infty]$
首先,如果$abs(a_{j}-a_{i})\leqslant abs(b_{i}-a_{i})$,显然选$a_{j}$更好,数据范围更大而且答案又更小
那如果是$|a_{j+1}-a_{i}|\leqslant |b_{i}-a_{i}|$(显然一定有一遍是满足小于等于的,,,这个太显然了趴$QwQ$),那如果取$a_{i}$,就相当于是范围变小了,但是值也变小了.考虑如果取的是$a_{j+1}$,对于下一个数,如果第$i+1$位的最优解大于等于$a_{j+1}$,那范围根本就不重要,显然$a_{j+1}$就是第$i$位的最优解,然后如果最优解小于$a_{j+1}$,显然的是这个亏了的部分能在$|b_{i}-a_{i}|-|a_{j+1}-a_{i}|$中找补回来,也就是说把$a_{j+1}$当最优解是不亏的
综上,可以证明,新的数列$b$一定都是原数列$a$中的数,$over$
以上证得非常扯淡,,,还是放下$TT$小朋友滴题解,,,里面有证明,,,挺详细的来着$QwQ$
总之呢,既然知道了这个结论,就能直接设$f_{i,j}$:$b_{i}$取$a_{j}$的最小代价,然后正着反着分别做一遍就好$QwQ$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
using namespace std;
#define il inline
#define gc getchar()
#define int long long
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int M=+;
int n,as,a[M],b[M],f[M][M],tmp; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:x;
}
il int abss(ri x){return x>?x:-x;}
il int work1()
{
memset(f,,sizeof(f));rp(i,,n)f[][i]=;
rp(i,,n)rp(j,,n){f[i][j]=f[i][j-];f[i][j]=min(f[i][j],f[i-][j]+abss(b[i]-a[j]));}
tmp=f[n][];rp(i,,n)tmp=min(tmp,f[n][i]);
return tmp;
}
il int work2()
{
memset(f,,sizeof(f));rp(i,,n)f[][i]=;
rp(i,,n)my(j,n,){f[i][j]=f[i][j+];f[i][j]=min(f[i][j],f[i-][j]+abss(a[j]-b[i]));}
tmp=f[n][];rp(i,,n)tmp=min(tmp,f[n][i]);
return tmp;
} main()
{
n=read();rp(i,,n)a[i]=b[i]=read();sort(a+,a++n);
as=work1();as=min(as,work2());
printf("%lld\n",as);
return ;
}
$D$
大致题意是,有$n$个人,每个人有2个属性$d_{i}$和$p_{i}$,然后要求从中选出$m$个数,最小化$\left | \sum d-\sum p \right |$,然后如果有多个相等的$ \left | \sum d - \sum p \right | $,就输出$\sum d + \sum p$最大的
显然考虑$dp$鸭,考虑设$f_{i,j}$表示选了$i$个人,$d-p=j$的$( \sum d + \sum p)_{max}$
大力转移就好?记录路径什么的懒得写辣自己瞎搞一通就好昂_(:з」∠)_
然后就做完辣?
$over?$
然后很迷的是我用$spj$拍了下,拍了几千组都对的,感$jio$稳得布星就交了,交了之后发现$WA$了,,,?我说不出来我想不明白$TT$
不管了我先放下我$code$,,,对了这题神仙$hl$写了$spj$如果卡在这题的可以向他要$spj$鸭$QwQ$
$upd:$我放弃辽$QAQ$,,,,我最后拿的神仙$hl$的$code$过了$QAQ$
所以就不放标程了,,,因为我也麻油打出来标程昂嘤嘤嘤
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int N=+,M=+;
int n,m,cas,as_p,as_d,as[M],f[M][N];
bool gdgs=;
struct node{int d,p,dat,sum;}nod[M]; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il void solve()
{
vector<int>pth[M][N];
rp(i,,n){ri x=read(),y=read();nod[i]=(node){x,y,x-y,x+y};}
memset(f,-,sizeof(f));f[][]=;
rp(i,,n)
{
my(j,m,)
{
rp(k,nod[i].dat,)
{
if(f[j][k]<f[j-][k-nod[i].dat]+nod[i].sum && f[j-][k-nod[i].dat]!=-)
{f[j][k]=f[j-][k-nod[i].dat]+nod[i].sum;pth[j][k]=pth[j-][k-nod[i].dat];pth[j][k].push_back(i);}
}
}
}
rp(i,,)
{
if(f[m][+i]!=-)
{
if(f[m][+i]>f[m][-i]){ri tmp=,sz=pth[m][+i].size();rp(j,,sz-)as[++tmp]=pth[m][+i][j];break;}
else{ri tmp=,sz=pth[m][-i].size();rp(j,,sz-)as[++tmp]=pth[m][-i][j];break;}
}
if(f[m][-i]!=-){ri tmp=,sz=pth[m][-i].size();rp(j,,sz-)as[++tmp]=pth[m][-i][j];break;}
}
rp(i,,m)as_d+=nod[as[i]].d,as_p+=nod[as[i]].p;
} int main()
{
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
while(gdgs)
{
n=read();m=read();if(!n && !m)return ;printf("Jury #%d\n",++cas);
solve();
printf("Best jury has value %d for prosecution and value %d for defence:\n",as_d,as_p);rp(i,,m)printf(" %d",as[i]);printf("\n\n");
}
return ;
}
$E$
多重背包板子鸭,,,?
既然复习了多重背包就$cue$下多重背包俩优化趴(虽然并不知道这题要不要$QwQ$?
一个是二进制分解;还一个是单调队列优化
都麻油太难不想写了,,,而且我应该之前写过来着$QwQ$?
昂然后这题,,,$gql$又呆了,,,越来越菜了$szd$,,,$QAQ$
先放下$gql$超呆的代码,,,打完就发现复杂度假得一批,,,显然过不去$QAQ$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i) const int N=+,M=+;
int n,m,c[N],a[N],as;
bool gdgs=,f[M]; il bool cmp(node x,node y){return x.st<y.st;}
il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
} int main()
{
while(gdgs)
{
memset(f,,sizeof(f));f[]=;as=;
n=read();m=read();if(!n && !m)return ;
rp(i,,n)a[i]=read();rp(i,,n)c[i]=read();
rp(i,,n)rp(j,,c[i])rp(k,,m-a[i])vis[k+a[i]]|=f[k];
rp(i,,m)if(f[i])++as;printf("%d\m",as);
}
return ;
}放下傻逼代码,,,呆死了$QAQ$
啊当然二进制分解优化下应该还是过得去,主要这个方法太呆了,,,所以不想说,还是说个正常点儿的想法$QAQ$
显然要考虑转化一下,,,比如$O(m\cdot n)$就还是能接受的,所以考虑怎么消除掉这个$c_{i}$
于是考虑加入一个数组$cd_{i,j}$表示要凑出数值$i$至少需要$j$种的数量,因为是一种种分开考虑的,所以显然可以降一维,直接$cd_{i,j}$就好,然后就转移的时候判一下需要的数量不超过$c_{j}$就好
然后就做完辣,,,?
真·$over$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i) const int N=+,M=+;
int n,m,c[N],a[N],as,cd[M];
bool gdgs=,f[M]; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
} int main()
{
while(gdgs)
{
memset(f,,sizeof(f));f[]=;as=;
n=read();m=read();if(!n && !m)return ;
rp(i,,n)a[i]=read();rp(i,,n)c[i]=read();
rp(i,,n){memset(cd,,sizeof(cd));rp(j,a[i],m)if(!f[j] && f[j-a[i]] && cd[j-a[i]]<c[i])f[j]=,cd[j]=cd[j-a[i]]+;}
rp(i,,m)if(f[i])++as;printf("%d\n",as);
}
return ;
}
$F$
中文可以看$luogu$上的$QwQ$
说个题外话.这题我印象中是个$dp$入门题昂,,,去年暑假谢总就讲过了的,,,?为什么在洛谷上有紫,,,?
瞎写下做法趴,显然考虑$f_{i,j}$,即$[i,j]$区间最大值,这时候发现有负负得正这种东西,于是考虑再存个$g$表示$min$嘛,然后就做完了,,,?
无脑转移就好$QwQ$?就$f_{i,j}=f_{i,k}\ operator_{k}\ f_{k+1,j}$,大概长这样儿,$g$什么的自己意会下就好,,,
然后环这个东西的处理,我开始很呆地想着枚举断哪条链然后$O(n^{4})$做,,,仔细思考下之后发现是我呆了$QAQ$直接倍长然后做就好$QAQ$
$over$
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstdio>
using namespace std;
#define il inline
#define int long long
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i) const int N=+,inf=1e9;
int n,f[N][N],g[N][N],as;
bool vis[N][N];
struct node{char op;int x;}nod[N<<]; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il char rdch(){rc ch=gc;while(ch!='t' && ch!='x')ch=gc;return ch;}
il int cal(ri x,ri y,char op){if(op=='t')return x+y;return x*y;}
il int maxx(ri x,ri y,ri p,ri q){return max(max(x,y),max(p,q));}
il int minn(ri x,ri y,ri p,ri q){return min(min(x,y),min(p,q));}
il void solve(ri l,ri r)
{
if(vis[l][r])return;
vis[l][r]=;f[l][r]=-inf;g[l][r]=inf;
ri tmp_f,tmp_g;
rp(i,l,r-)
{
solve(l,i);solve(i+,r);ri mx,mn;
mx=maxx(cal(f[l][i],f[i+][r],nod[i+].op),cal(f[l][i],g[i+][r],nod[i+].op),cal(g[l][i],f[i+][r],nod[i+].op),cal(g[l][i],g[i+][r],nod[i+].op));
mn=minn(cal(f[l][i],f[i+][r],nod[i+].op),cal(f[l][i],g[i+][r],nod[i+].op),cal(g[l][i],f[i+][r],nod[i+].op),cal(g[l][i],g[i+][r],nod[i+].op));
if(mx>f[l][r])tmp_f=i;if(mn<g[l][r])tmp_g=i;
f[l][r]=max(f[l][r],mx);g[l][r]=min(g[l][r],mn);
}
} main()
{
freopen("4342.in","r",stdin);freopen("4342.out","w",stdout);
n=read();rp(i,,n)nod[i]=nod[i+n]=(node){rdch(),f[i][i]=g[i][i]=f[i+n][i+n]=g[i+n][i+n]=read()},vis[i][i]=vis[i+n][i+n]=;
solve(,n<<);
as=-inf;rp(i,,n)as=max(as,f[i][i+n-]);printf("%lld\n",as);rp(i,,n)if(as==f[i][i+n-])printf("%lld ",i);
return ;
}
$G$
占坑,晚上写$QwQ$
$H$
感$jio\ H$题好像比较水的样子$(bushi$,所以先来搞下$H$题嘻嘻
题意懒得放了,这种题目,显然看到就应该想到设$f_{i,j,0/1}$?就前$i$段时间休息了$j$段时间,然后第$i$段的状态是不是在睡觉.瞎转移一通就好,,,?
$over$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int N=4e3+;
int n,b,f[N][],u[N],as; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il void solve(){rp(i,,n)my(j,b,)f[j][]=max(f[j][],f[j][]),f[j][]=max(f[j-][],f[j-][]+u[i]);}
int main()
{
n=read();b=read();rp(i,,n)u[i]=read();
memset(f,,sizeof(f));f[][]=f[][]=;solve();as=max(f[b][],f[b][]);
memset(f,,sizeof(f));f[][]=u[];solve();as=max(as,f[b][]);
printf("%d\n",as);
return ;
}
$I$
题目大意就,有个机器人位于$(x,y)$,然后有四种操作,留在原地/向左/向右/向下,然后如果到了边界就不能再往边界上走了(即第一列不能往左走第$m$列不能往右走嘛$QwQ$),问到达最后一行的期望操作数是多少$QwQ$
不难列出转移式,就$f_{i,j}=\frac{f_{i,j}+f_{i,j+1}+f_{i,j-1}+f_{i+1,j}}{4}+1$(边界有点儿不一样,,,差不多就懒得另外写个了,,,推出来还是很$easy$的来着$QAQ$
然后这时候发现,就不管是按哪个顺序,$f_{i,j+1}$和$f_{i+1,j}$是一定有一个还麻油推出来的,,,$QAQ$
然后理论上是有两种处理方式的?
一个可以用高斯消元搞掉,一个比较基本的套路来着$QwQ$?
但还是大概港下好辣_(:з」∠)_
不难发现,每一行之间是无后效性的,相互影响的只有同一行的,也就是说当求$f_{i}$的时候$f_{i+1}$其实是已知的
于是考虑把$f_{i}$的式子都列出来,不难发现就变成了一个$m$元一次方程组,然后还刚好给了$m$个式子,那显然就可以解出来了昂$QwQ$
然后因为显然每个高斯消元系数矩阵每行都只会有2/3个地方有数,,,所以在$O(M)$的时间内就能解出来,,,所以复杂度是$O(MN)$的,还是挺稳的来着$QwQ$
另一个可以观察下这个式子,,,不难发现其实是有点儿规律的,,,所以找下规律($bushi$可以发现一个三角形式这种,然后就能线性算辣$QwQ$,,,
懒得详细写了,,,就只瞎写点儿东西,,,就很显然能发现答案一定是对称分布的,所以瞎推下就搞完辣,,,瞎扯的,瞎$get$下就好$QAQ$
$upd:$
做完辣!来记录几个坑$QAQ$
第一个是要防止控制数据范围防止爆炸,,,
主要可能是因为我不记得高斯消元板子辽,就手推了下,直接打了上去.
然后放这题,因为和普通的高斯消元实际上还是有点儿区别(否则复杂度会爆炸,,,$QAQ$
所以我的方法是,正着做一遍,就每行只会有1/2个地方有数了,再倒着做一遍,就每行只会有一个地方有系数了,就做完了
但是这儿要$attention$的是,如果直接做,比如我开始是这么打的(这是正着做一遍的$code$):
$g[i+1][m+1]=-(lf)g[i][i]/g[i+1][i]*g[i+1][m+1]+g[i][m+1];$
$g[i+1][i+1]=-(lf)g[i][i]/g[i+1][i]*g[i+1][i+1]+g[i][i+1];$
这么做的直接后果就是系数增长飞快,,,瞬间爆炸$QAQ$
所以在过程中一定要控制$g_{i,i}$那一位=1!就没事儿除下就好!
(反着做的时候一样儿,,,懒得写辣$QAQ$
第二个是要注意下$m=1$的情况
这个时候的式子是:$f_{i,1}=f_{i+1,1}+2$(懒得写过程了直接放变形之后的结果_(:з」∠)_
然后直接就可以得到$as=2\cdot (n-x)$,直接输出就好
$over$
然后就放下代码,,,因为我高斯消元掌握得不怎么好,所以给自己补了个注释,防止以后重看看不懂了$QAQ$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define lf double
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int N=+;
int n,m,x,y;
lf f[N][N];
/*高斯消元!*/
struct gdgs
{
lf g[N][N];
il void clr(){memset(g,,sizeof(g));}
il void solve()
{
/*这就正着做一遍嘛QwQ*/
rp(i,,m-)
{
/*这个就坑点里港的那个,防爆炸的code*/
g[i][m+]=(lf)g[i][m+]/g[i][i];g[i][i+]=(lf)g[i][i+]/g[i][i];g[i][i]=;
/*这个就坑点里港的那个,防爆炸的code*/
g[i+][m+]=-(lf)g[i][i]/g[i+][i]*g[i+][m+]+g[i][m+],g[i+][i+]=-(lf)g[i][i]/g[i+][i]*g[i+][i+]+g[i][i+];
if(i!=m-)g[i+][i+]=-(lf)g[i][i]/g[i+][i]*g[i+][i+];
g[i+][i]=;
}
/*这就正着做一遍嘛QwQ*/ /*这就反着做一遍嘛QwQ*/
my(i,m,)
{
/*这个就坑点里港的那个,防爆炸的code*/
g[i][m+]=(lf)g[i][m+]/g[i][i];g[i][i-]=(lf)g[i][i-]/g[i][i];g[i][i]=;
/*这个就坑点里港的那个,防爆炸的code*/
g[i-][m+]=-(lf)g[i][i]/g[i-][i]*g[i-][m+]+g[i][m+];g[i-][i-]=-(lf)g[i][i]/g[i-][i]*g[i-][i-];g[i-][i]=;
}
/*这就反着做一遍嘛QwQ*/
}
}lq;
/*高斯消元!*/ il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il void dp(ri x)
{
/*瞎赋下系数嘛,懒得详细写了自己手推下就出来了,,,*/
lq.clr();lq.g[][]=;lq.g[][]=-;lq.g[m][m-]=-;lq.g[m][m]=;lq.g[][m+]=f[x+][]+;lq.g[m][m+]=f[x+][m]+;
rp(i,,m-)lq.g[i][i-]=lq.g[i][i+]=-,lq.g[i][i]=,lq.g[i][m+]=+f[x+][i];
/*瞎赋下系数嘛,懒得详细写了自己手推下就出来了,,,*/
lq.solve();
rp(i,,m)f[x][i]=(lf)lq.g[i][m+]/lq.g[i][i];
} int main()
{
// freopen("i.in","r",stdin);freopen("i.out","w",stdout);
n=read();m=read();x=read();y=read();
if(m==)return printf("%.6lf\n",(lf)*(n-x)),;
my(i,n-,x)dp(i);
printf("%.6lf\n",f[x][y]);
return ;
}
/*最后还要注意一个,,,就不要开long double昂,,,会T的QAQ */
$J$
首先考虑先判掉为0的$as$?不难想到除了面积为奇数以外不可能存在$as$的0的了,就可以先特判掉$QwQ$
考虑当第$i$行已经被填补满了的时候,因为有可能有的格子是竖着放的,就导致第$i+1$行实际上是千疮百孔的,$so$如果常规地设个$f_{i,j}$表示填到第$i$行第$j$个格子这样显然是不可取的$QAQ$,显然考虑状压下,设$f_{i,S}$表示前$i-1$行都已经填完了,然后第$i$行的状态是$S$时候的方案数
那转移就相当好想了?枚下这一行状态再枚这一行,然后判下是否合法转移下就做完辽$QwQ$
$upd:$还有个神仙方法,,,可以用轮廓线$dp$,,,然而太神仙了,,,菜鸡$gql$学不会$QAQ$
神仙$TT$写了题解,我就只放下她题解好了/kel/kel/kel
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define gc getchar()
#define int long long
//#define int __int128
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int N=;
int f[N][<<N],n,m;
bool gdgs=; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il bool pre(ri zt)
{
for(ri i=;i<m;)if(zt&(<<i)){if(i==m- || (!(zt&(<<(i+)))))return ;i+=;}else ++i;
return ;
}
il bool check(ri nw_zt,ri pre_zt)
{
// if(nw_zt==3 &&)
for(ri i=;i<m;)
{
if(nw_zt&(<<i))
{
if(pre_zt&(<<i))
{
// if(nw_zt==3 && pre_zt==13 && i==0)printf("???daraoleQAQ pre&2=\n");
if(i==m- || (!(nw_zt&(<<(i+)))) || (!(pre_zt&(<<(i+)))))return ;
i+=;
}
else ++i;
}
else{if(!(pre_zt&(<<i)))return ;++i;}
}
return ;
} il int dp()
{
ri tot=(<<m)-;memset(f,,sizeof(f));
// printf("tot=%lld\n",tot);
rp(s,,tot)if(pre(s))f[][s]=;//,printf("s=%lld\n",s);
rp(i,,n)rp(j,,tot)rp(k,,tot)
if(check(j,k))f[i][j]+=f[i-][k];//,printf("i=%lld j=%lld k=%lld\n",i,j,k);
return f[n][tot];
} main()
{
// freopen("j.in","r",stdin);freopen("j.out","w",stdout);
while(gdgs)
{
n=read(),m=read();
if(!n && !m)return ;if((n*m)&){printf("0\n");continue;}
if(n<m)swap(n,m);printf("%lld\n",dp());
}
return ;
}
$K$
一个非常显然的状压$dp$?显然考虑设$f_{i,j,k}$表示第$i$行然后这一行的状态是$j$上一行的状态是$k$时候的最多炮兵数,滚掉一维然后瞎转移下就好$QwQ$
然后一个小优化是,考虑给每个位置赋值,这个值指的就,这个位置附近最近能放炮兵的位置离它的距离,举个$eg$昂,就比如一个十字架,就会是这样儿的
0
1
0 1 2 1 0
1
0
转移啥的都差不多,但这样儿每行状态就可以压缩成一个三进制数辣,就可以省点儿空间$QwQ$
$upd:$,,,我枯了$sd\ gql$又双叒傻逼了,,,就法一的状态显然不需要那么设,,,因为已经预处理辣,所以$j$和$k$只要记录是哪一个就好,,,显然数量并不多,,,我开的100是够的$QwQ$
$over$,放个$code$鸭
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define lf double
#define fi first
#define sc second
#define gc getchar()
#define mp make_pair
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int N=+,M=;
int n,m,f[N][M][M],zt[N],gdgs_num,as;
char str[M];
vector< pair<int,int> >gdgs; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il int cal(ri x){ri num=;rp(i,,m-)if(x&(<<i))++num;return num;}
il bool check(ri x){ri pre=-;rp(i,,m-)if(x&(<<i))if(i-pre>)pre=i;else return ;return ;}
il void pre(){rp(i,,(<<m)-)if(check(i))gdgs.push_back(mp(i,cal(i)));gdgs_num=gdgs.size();}
il bool jud(ri i,ri j,ri k){if(gdgs[i].fi&gdgs[j].fi || gdgs[j].fi&gdgs[k].fi || gdgs[i].fi&gdgs[k].fi)return ;return ;}
il void print(ri x){my(i,m-,)printf("%d",x&(<<i)?:);} int main()
{
freopen("k.in","r",stdin);freopen("k.out","w",stdout);
n=read();m=read();pre();
rp(i,,n){scanf("%s",str+);rp(j,,m)zt[i]=((zt[i]<<)+(str[j]=='H'));}
rp(i,,gdgs_num-)if(!(zt[]&gdgs[i].fi))f[][i][]=gdgs[i].sc;
rp(i,,n)
{
rp(j,,gdgs_num-)
{
rp(k,,gdgs_num-)
{
if(zt[i-]&gdgs[k].fi || zt[i]&gdgs[j].fi)continue;
rp(p,,gdgs_num-)
{
if(jud(j,k,p))f[i][j][k]=max(f[i][j][k],f[i-][k][p]+gdgs[j].sc);
}
}
}
}
rp(i,,gdgs_num-)rp(j,,gdgs_num-)as=max(as,f[n][i][j]);printf("%d\n",as);
return ;
}
$L$
似乎是水题鸭
只要不被题意杀就还是不太难的_(:з」∠)_(虽然$sd$如$gql$依然因为$get$错题意$WA$了一发嘤嘤嘤
昂所以还是先港下题意趴$QAQ$
大概就说,有$n$头奶牛,要覆盖$[1,T]$段,每个奶牛可以覆盖$[l_{i},r_{i}]$,然后问最少要多少头奶牛
$umm$不就是个贪心入门题,,,?任务安排问题还是叫什么,忘了_(:з」∠)_
就瞎拍个序瞎搞一通就好,贪心正确性过于显然不证辣$QAQ$
然后就放个代码就欧克克辣!
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i) const int inf=1e9;
int n,i,j,T,as,nw,nw_tr;
bool fd;
struct node{int st,en;}p[+]; il bool cmp(node x,node y){return x.st<y.st;}
il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
} int main()
{
n=read();T=read();
rp(i,,n)p[i]=(node){read(),read()};sort(p+,p++n,cmp);p[n+].st=inf;
rp(i,,n)
if(p[i].st<=nw_tr+)
{
if(p[i].en>nw)nw=p[i].en,fd=;
if(p[i+].st>nw_tr+ && fd)nw_tr=nw,++as,fd=;
}
if(nw_tr<T)printf("-1\n");else printf("%d\n",as);\
return ;
}
$M$
好气昂,,,我本来以为我做过这道题来着,,,然后找了半天并没有找到题解$or$做题记录/$kel$/$kel$/$kel$
所以还是写下这题趴$QAQ$
题意是港,有$k$个人给$n$块栅栏涂色,第$i$个人最多只能从$s_{i}$这个位置开始连续涂$l_{i}$块栅栏,可以一个也不涂,然后每个人涂一块的工钱是$p_{i}$.求最大工钱$QwQ$
显然考虑$dp$?设$f_{i,j}$表示第$i$个人涂到$j$这个位置时候最大工钱
转移也十分显然?$f_{i,j}=(f_{i-1,k}+p_{i}\cdot (j-k+1))_{max}$
然后这样儿就,是$O(K\cdot M^{2})$的
考虑单调队列优化下,就欧克辣$QwQ$
因为菜菜$gql$单调队列学得不太好,,,所以还是仔细港下$QAQ$,然后先港下,为了表示方便,就先把$f_{i,j}$暂时简化成$f_{j}$
考虑瞎变形一通呗$QwQ$,
$f_{j}=(f_{k}+p_{i}\cdot (j-k+1)){max}$
$f_{j}=p_{i}\cdot (j+1)+(f_{k}-p_{i}\cdot k)_{max}$
因为每次转移到时候可以当做是$i$和$j$是恒量,也就是说变量只有$k$,于是显然单调队列搞下后面那个$max$就做完辣$QwQ$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define lf double
#define fi first
#define sc second
#define gc getchar()
#define mp make_pair
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int K=+,N=+;
int n,k,que[N],head,tail,f[K][N];
struct node{int l,p,s;}nod[K]; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il bool cmp(node gd,node gs){return gd.s<gs.s;} int main()
{
// freopen("m.in","r",stdin);freopen("m.out","w",stdout);
n=read();k=read();rp(i,,k)nod[i].l=read(),nod[i].p=read(),nod[i].s=read();sort(nod+,nod++k,cmp);
rp(i,,k)
{
head=;tail=;
rp(j,max(,nod[i].s-nod[i].l),nod[i].s-)
{while(head<=tail && f[i-][que[tail]]-nod[i].p*que[tail]<=f[i-][j]-nod[i].p*j)--tail;que[++tail]=j;}
rp(j,,n)
{
f[i][j]=max(f[i-][j],f[i][j-]);
if(j>=nod[i].s){while(head<=tail && que[head]<j-nod[i].l)++head;if(head<=tail)f[i][j]=max(f[i][j],f[i-][que[head]]+nod[i].p*(j-que[head]));}
}
}
printf("%d\n",f[k][n]);
return ;
}
/*
有一个长度为n的[1,n]墙
有k位工人
第i位工可以刷包含si的长度小于等于li的区间,报酬为区间长度乘以pi
*/
$N$
先港下题意鸭$QwQ$
就说给定$n$个数$a_{1,...,n}$,然后要求分为若干块,使得每块的$sum\leq m$,然后$\sum max_{min}$
先引入几个变量,$mx_{i,j}$表示$i$到$j$的$max$
显然考虑$dp$,,,$f_{i}=(f_{j}+mx_{j+1,i})_{min}$
因为$f_{i}$和$mx_{i,j}$显然都是递增的,然后因为有$m$的限制于是$j$也是递增的,于是考虑单调队列优化?
然后就做完咯,,,?
依然因为$gql$太菜了于是考虑详细港下趴$QAQ$
因为$f_{i}$是单增的,于是不难想到,如果在区间$[l,r]$内,满足$r$为最大值,则有$f_{l}+a_{r}\leq f_{l+1}+a_{r}\leq ... \leq f_{r-1}+a_{r}$,所以一定是取$f_{l}$,也就是说,对于一段$l,r$,如果能满足$r$是其中的$max$,则会有取$l$时是最小的,换句话说,考虑构造一个$a_{i}$递减的序列$que_{j}$,每次只要把$que_{j}+1$拿出来就好(然后为了第一个数能表示出来所以要$que_{1}=0$,,,当然这种$just$小细节并不重要的辣$QwQ$
但这儿还有个问题要注意下昂$QwQ$,,,就,这里显然不能满足队列的队首就是最优的,,,队列里的每个数都能构成最优解来着$QAQ$,所以可以考虑再引入一个数据结构能维护一下这个$f_{i-1}+a_{i}$的$min$,然后还呲呲删除操作,显然考虑平衡树?然后用平衡树太麻烦辣,,,于是考虑用$set$就好了鸭$QwQ$
$over$?
挺妙的我$jio$得还,,,也许会$new$一篇文章专门给写个题解呢$QAQ$
对了记得开$ll$不然会$WA$嘤嘤嘤
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define il inline
#define lf double
#define fi first
#define sc second
#define gc getchar()
#define int long long
#define mp make_pair
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int N=+;
int n,m,a[N],tail,head=,que[N],sum[N],nw,f[N];
multiset<int>s;
multiset<int>::iterator it; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
} main()
{
// freopen("n.in","r",stdin);//freopen("n.out","w",stdout);
n=read();m=read();
rp(i,,n)
{
sum[i]=sum[i-]+(a[i]=read());if(a[i]>m)return printf("-1\n"),;
while(sum[i]-sum[nw]>m)++nw;
while(head<=tail && que[head]<=nw){if(head<tail)s.erase(s.find(f[que[head]]+a[que[head+]]));++head;}
while(head<=tail && a[que[tail]]<=a[i]){if(head<tail)s.erase(s.find(f[que[tail-]]+a[que[tail]]));--tail;}
que[++tail]=i;if(head<tail && i>que[tail-])s.insert(f[que[tail-]]+a[i]);
f[i]=f[nw]+a[que[head]];if(head<tail)f[i]=min(f[i],*s.begin());
}
printf("%lld\n",f[n]);
return ;
}
$O$
好像题目和$p$题差不多,,,?
那应该就差不多套路,,,不说辣$QwQ$
昂不过要$mk$下好像$get$了一个奇奇怪怪的方法$hhhh$
就,观察到这个$s$的范围是50以内的,相当于差不多一个批次执行时间的$1/200$?然后就说明暗示一个批次最多200个,,,限制下搜索分支就水过去辣,,,听起来很有趣的样子$hhhh$
$P$
语文不好选手没有人权$TT$,,,我我我我看了半天才看懂题,,,哭了$TT$
但是懒得解释题意辣,估计也只有我一个$sd$连题目都看不懂趴$QAQ$
然后直接看题嗷,显然考虑就直接设$dp$式了鸭,就$f_{i}$:第$j$个物品安排完了的最少花费
昂接下来为了表示方便,引入两个新变量,一个$st_{i,j}$表示$i$到$j$的所有物品的合计时间,还一个$sc_{i,j}$表示$i$到$j$的所有物品的合计花费
转移过于显然?$f_{i}=(f_{j}+st_{1,i}\cdot sc_{k+1,i}+s\cdot sc_{j,n})_{min}$
然后这样儿就$O(n^{3})$的?显然复杂度过不去,于是考虑斜率优化
明儿会写斜率优化专题的这儿就先咕辣_(:з」∠)_
$Q$
然后翻译的话洛谷上有,然而,洛谷的翻译简直有点儿莫名其妙,,,我理解了半天,,,虽然可能是我语文太差于是理解能力超差$QAQ$?不管反正就还是放个题目大意昂$QAQ$
大概就是说有$m$只猫分布在$n$座山上,现在能走$p$趟,每趟能带走所有能带走的猫,能带走的定义是,每只猫有一个对应的$t_{i}$,当到达这只猫所在山丘的时间$T_{i}\geq t_{i}$的时候就能带走第$i$只猫,然后到达山丘的时间是出发时间$T$加上从第一座山到第$j$座山的时间$D_{j}-D_{1}$,求最小化$\sum T_{i}-t_{i}$
鸭我发现我依然解释得很冗杂,,,
再简略一点好了?就给定$n,m,p,t_{m},D_{n}$,求最小化$\sum T_{j}+D_{i}-D_{1}-t_{i}$,其中$T_{j}$能有$p$个取值,且有$T_{j}+ D_{i} - D_{1} \geq t_{i}$
欧克反正这是我能达到的最简略辽,,,看题趴$QAQ$
既然是$dp$专题那就直接上手考虑$dp$趴,也不难想到,肯定先对猫按$t_{i}$排序,然后设$f_{i,j}$表示第$i$个人带走前$j$只猫的最小$as$,转移就枚举上一个人带到了第多少只,$f_{i,j}=(f_{i-1,k}+(j-k)\cdot t_{j}-sum_{k+1,j})_{min}$,昂这个$sum_{i,j}$是我引入的一个新变量,指的就$\sum_{k=i}^{j}t_{k}$
然后考虑时间复杂度,不难发现这样儿是$O(n^{2})$的,显然要优化,不难变形得$f_{i,j}=(f_{i-1,k}+sum_{k+1}-k\cdot t_{j})_{min}+j\cdot t_{j}-sum_{j}$,于是考虑斜率优化?
瞎写下斜率优化趴,,,我试着想了一下才发现我完全不会斜率优化昂,,,天呐$gql$实菜了$TT$
首先变形,后面为了表达方便,都压一位,即$f_{i,j}\rightarrow f_{j}$,然后把常数项先不管
瞎变形一通,设$g_{i}=sum_{i+1}+f_{i}$,于是得$k\cdot t_{j}+f_{j}=g_{k}$,考虑将$t_{j}$看做$K$,$g_{k}$看做$Y$,$k$看做$X$(,,,我错了我命名有点重,,,算了懒得改了大概能$get$就好$QAQ$
于是$f_{j}$就是$B$,于是就变成了,有若干条直线,斜率都是$t_{j}$,过点$(k,g_{k})$的最小截距(,,,大概是这么理解的,,,?具体解释咕不咕随缘,,,想起来了再更$QAQ$
然后就是个斜率优化经典题辣,到这儿还是不难辣$QAQ$
然后放个代码趴$QwQ$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define int __int64
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int N=+,M=+;
int n,m,p,sum[N],d[N],a[N],f[M][N],tp,hd,stck[N]; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
void print(int x)
{
if(x>)print(x/);
putchar(''+x%);
} main()
{
// freopen("q.in","r",stdin);//freopen("q.out","w",stdout);
n=read();m=read();p=read();
rp(i,,n)d[i]=read()+d[i-];
rp(i,,m){ri x=read(),y=read();a[i]=y-d[x];}
sort(a+,a+m+);
sum[]=;rp(i,,m)sum[i]=sum[i-]+a[i];
rp(i,,m)f[][i]=i*a[i]-sum[i];
rp(i,,p)
{
stck[hd=tp=]=;
rp(j,,m)
{
while(hd<tp && f[i-][stck[hd+]]+sum[stck[hd+]]-(f[i-][stck[hd]]+sum[stck[hd]])<(stck[hd+]-stck[hd])*a[j])++hd;
f[i][j]=f[i-][stck[hd]]+(j-stck[hd])*a[j]-(sum[j]-sum[stck[hd]]);
stck[++tp]=j;
while(hd<tp-
&&
(f[i-][stck[tp]]+sum[stck[tp]]-(f[i-][stck[tp-]]+sum[stck[tp-]]))
*
(stck[tp-]-stck[tp-])
<
(f[i-][stck[tp-]]+sum[stck[tp-]]-(f[i-][stck[tp-]]+sum[stck[tp-]]))
*
(stck[tp]-stck[tp-])){--tp;stck[tp]=stck[tp+];}
}
}
print(f[p][m]);
return ;
}(嗷对了,,,就,因为我之前$WA$了一次,就在$cf$上套了下数据,然后就$get$了第二组数据,还是算比较大样例辽,放上来存下如果$WA$了的可以拿这个数据测下$QwQ$
这是数据$QwQ$
$T$
因为是英文于是先放个题目大意趴$QAQ$
就说给定一个数字$n$,然后求按字典序排列后满足条件的第$k$大的数列长什么样儿,然后条件是说要求每个数两边的两个数要么都比它高要么都比它低,简单来说就,会成一个高低高低高低这样的排列
看到这个就会想到之前寒假的时候考试的那道题?1.22 T5,差不多想法,就预处理出这个点放这个数的方案数,然后就直接查询就好
具体这道题的话就预处理两个数组,$f_{i,j}$和$g_{i,j}$,分别表示(在这个长度为$i$的序列中)排名为$j$的数在第一个长度为$i$且是上升开头的方案数和第1个数的排名为$i$长度为$j$且是下降开头的方案数,做一通就好$QwQ$
然后就做完辣辣辣!
$upd$下关于转移中的一个小问题,就,为什么转移$g$的时候$k$是从$j$开始枚举而不是从$j+1$开始枚举的呢
这个是要结合$g$的意义看的,就因为它的$j$表示的是相对排名,然后要转移到$f_{i,j}$的话,就相当于要排除掉$j$这个数了,那么$[j+1,i]$的所有数的排名都会跌一位,也就是说原本排名是$j+1$的,在新的排名中就会是$j$了,因此其实从$j$开始枚举在$f_{i-1}$中的意义就相当于是从$j+1$开始枚举的$QwQ$
欧克我$jio$得现在的理解应该是最能理解的了,,,?如果还有问题在评论里港就好$kk$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define int long long
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int N=+;
int n,k,f[N][N],g[N][N],vis[N]; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il void pre()
{
f[][]=g[][]=;
rp(i,,)
rp(j,,i)
{
rp(k,,j-)f[i][j]+=g[i-][k];
rp(k,j,i-)g[i][j]+=f[i-][k];
}
}
il void output()
{
memset(vis,,sizeof(vis));
ri pre,prepre;
rp(i,,n)
{
ri cnt=;
rp(j,,n)
if(!vis[j])
{
ri tmp=k;++cnt;
if(i==)k-=f[n-i+][cnt]+g[n-i+][cnt];
else
{
if(j>pre && (i== || prepre>pre))k-=f[n-i+][cnt];
else if(j<pre && (i== || prepre<pre))k-=g[n-i+][cnt];
}
if(k<=){vis[j]=;printf("%lld ",j);prepre=pre;pre=j;k=tmp;break;}
}
}
printf("\n");
} main()
{
// freopen("t.in","r",stdin);freopen("t.out","w",stdout);
pre();ri T=read();
while(T--){n=read(),k=read();output();}
return ;
}放个代码鸭$QwQ$
$upd:$
,,,$gql$傻逼石锤了$QAQ$
$O(n^{2})$简直不用脑子的优化我居然没想到,,,傻了吧唧地打了个$O(n^{3})$的傻逼玩意儿嘤嘤嘤
懒得改这道了,不过这题有个叫地精部落的双倍经验,我我我我只放下那题的$code$了昂$QwQ$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int N=+;
int n,k,f[][N],g[][N],mod,as;
bool gdgs=; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il int ad(ri x,ri y){return x+y>=mod?x+y-mod:x+y;}
il void work()
{
f[gdgs][]=g[gdgs][]=;
rp(i,,n)
{
gdgs^=;
f[gdgs][]=g[gdgs][]=;rp(k,,i-)g[gdgs][]=ad(g[gdgs][],f[gdgs^][k]);
if(i==n)as=(as+g[gdgs][])%mod;
rp(j,,i)
{
f[gdgs][j]=(f[gdgs][j-]+g[gdgs^][j-])%mod;g[gdgs][j]=((g[gdgs][j-]-f[gdgs^][j-])%mod+mod)%mod;
if(i==n)as=(as+f[gdgs][j]+g[gdgs][j])%mod;
}
}
} int main()
{
n=read();mod=read();work();printf("%d\n",as);
return ;
}又一个$upd:$
神仙$hl$之前过来问了下$f_{i,j}$和$g_{i,i-j+1}$是不是相同的,然后我想了下之后感觉没想通,就没仔细想了
然后后面他去问了神仙$yyb$,然后$yyb$就港了一个只用开一个数组的方法,,,就用了上面那个思想
先放下这个方法,最后写过程趴$QwQ$
其实现在由我上面那个$upd$就能发现,现在关于这个$f_{i,j}$的式子就变成了,$f_{i,j}=f_{i,j-1}+g_{i-1,j-1}$
然后因为有$g_{i,j}=f_{i,i-j}$,于是有$f_{i,j}=f_{i,j-1}+f_{i-1,i-j+1}$
不太会证,大概数学归纳法可以,,,?感性理解不难发现确实如此$bushi$
嗷对了,这个还有一种理解方法,有时间写,然后还有一种组合数的方法,$xzy$学长还发到题解区了,,,但我还没看,,,有时间再搞趴$QAQ$
$U$
数位$dp$板子,,,没什么好说的鸭$QwQ$套路一波就完事辣,,,
懒得仔细写辣,,,大概就$dfs$一下,记录几个参数,瞎做一通,就欧克辣$QwQ$
$upd:$
啊我呆了嘤嘤嘤,,,
这题连$dfs$都不用来着,,,直接顺推是麻油问题的来着$QwQ$
不过我都打了$dfs$了还是打完趴$QAQ$
只是说下,我到时候放上来的代码会非常呆,因为直接顺推能解决的问题,被我搞成了一个$dfs$嘤嘤嘤
$over$
然后还有就是这题本来应该是试填法,,,?但我懒得打了就直接打了个二分$QwQ$
对了,这篇$code$有个$bug$,我知道怎么解决但不知道为什么,,,
就我前面有个$for$循环,是这么写的,"mx=lim?a[pos]:9;rp(i,0,mx){}",这样儿就是欧克的
但是我最开始是这么打的"rp(i,0,lim?a[pos]:9)",然后莫名其妙的$i$就感觉没约束似的不停地增增增增然后就$T$了,,,
我也不知道为什么,,,之前好像也遇到过来着,,,但一直不清楚为什么嘤嘤嘤
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define int long long
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) const int N=;
int a[N],f[N][]; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il int power(ri gd,ri gs){ri ret=;while(gs){if(gs&)ret=1ll*ret*gd;gd=1ll*gd*gd;gs>>=;}return ret;}
il int solve(ri pos,ri state,rb lim)
{
if(!pos)return ;if(!lim && f[pos][state])return f[pos][state];
ri ret=,mx=lim?a[pos]:;rp(i,,mx){if(state== && i==)continue;ret+=solve(pos-,i==?state+:,i==a[pos] && lim);}
return lim?ret:f[pos][state]=ret;
}
il int pre(ri x){ri tmp=,tmpp=x;while(x){a[++tmp]=x%;x/=;}return tmpp-solve(tmp,,)+;} main()
{ri T=read();while(T--){ri x=read();ri l=,r=66666666666ll;while(l<r){ri mid=(l+r)>>;if(pre(mid)<x)l=mid+;else r=mid;}printf("%lld\n",l);}}
$V$
这题我没用$dp$辣,,,就感觉好像有什么规律,就瞎想了一下,发现$as$显然是$2^{n-1}+2^{n-1}\cdot (n-1)/2$?瞎解释下就,首先是有$2^{n-1}$个数的,然后对于首位,必须是1,于是就是$2^{n-1}$,然后对于之后的,就都是有一半是1,于是就是$2^{n-1}/2\cdot (n-1)$,$over$
当然递推什么的也能做,,,懒得想了$QAQ$
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define il inline
#define int long long
#define gc getchar()
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i) il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
} main()
{
// freopen("v.in","r",stdin);freopen("v.out","w",stdout);
ri T=read(),n,m;
while(T--){n=read();m=<<(n-);printf("%lld\n",m+m*(n-)/);}
return ;
}
随机推荐
- 删除username的索引
-- 删除index_name 索引 drop index index_name on user; show index from user \G; -- 创建新索引列组成,index_pinyin为 ...
- 第四次C++
继承与派生 一.什么是继承和派生 所谓继承就是从先辈处得到属性和行为特征.类的继承,是新的类从已有类那里得到已有的特性.从另一个角度来看这个问题,从已有类产生新类的过程就是类的派生.类的继承与派生机制 ...
- 2019-8-31-dotnet-通过-WMI-获取系统信息
title author date CreateTime categories dotnet 通过 WMI 获取系统信息 lindexi 2019-08-31 16:55:59 +0800 2019- ...
- 实现菜单底部线条沿着 X 轴的值缩放转换scaleX
效果: 代码: a{padding: 10px 10px; position: relative;} a:before{content: ''; width: 100%; height: 3px; b ...
- 不插字段,直接利用OracleSpatial计算
select to_char(regexp_replace(sdo_util.to_gmlgeometry(sdo_geom.sdo_difference( SDO_GEOMETRY ( 2003, ...
- Python--day69--ORM正反向查找(外键)
ForeignKey操作 正向查找 对象查找(跨表) 语法: 对象.关联字段.字段 示例: book_obj = models.Book.objects.first() # 第一本书对象 prin ...
- Python--day67--Django的路由系统
原文:https://www.cnblogs.com/liwenzhou/articles/8271147.html Django的路由系统 Django 1.11版本 URLConf官方文档 URL ...
- cp拷贝
1 cp 拷贝.复制 NAME cp - copy files and directories SYNOPSIS cp [OPTION]... [-T] SOURCE DEST -- c ...
- 2019-9-2-git-需要知道的1000个问题
title author date CreateTime categories git 需要知道的1000个问题 lindexi 2019-09-02 10:12:31 +0800 2018-2-13 ...
- JavaScript中Number数字数值浮点运算有误差
JavaScript浮点运算的一个bug. 比如:7*0.8 JavaScript算出来就是:5.6000000000000005 //调用:numberExcept(arg1,arg2) //返回值 ...