终于写完啦!!!!!!!!
T28 最大子段和
传送门
维护前缀和
找前面最小的
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=2e5+5;
const int INF=2147483647;
int n,a[N],p[N];
int main(){
n=in;
for(int i=1;i<=n;++i) a[i]=a[i-1]+in;
int tmp=INF,id=0;
for(int i=1;i<=n;++i){
p[i]=id;
if(a[i]<tmp) id=i,tmp=a[i];
}
tmp=-INF;
for(int i=1;i<=n;++i) tmp=max(tmp,a[i]-a[p[i]]);
printf("%d\n",tmp);
return 0;
}
交上去一波发现80pts
究其原因:只关注前面最值不够
考虑贪心
维护前缀和,但是前缀和不优秀就直接取此数
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=2e5+5;
const int INF=2147483647;
int n,a[N],p[N],ans=-INF;
int main(){
n=in;
for(int i=1;i<=n;++i) a[i]=in;
for(int i=1;i<=n;++i){
p[i]=max(p[i-1]+a[i],a[i]);
ans=max(ans,p[i]);
}
cout<<ans<<endl;
return 0;
}
T29 路径计数2
Portkey
棋盘型DP
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=1e3+5,M=1e5+5,mod=1e5+3;
int n,m,f[N][N];
bool b[N][N];
int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
int main(){
n=in,m=in;
for(int i=1;i<=m;++i){
int x=in,y=in;
b[x][y]=true;
}
f[1][1]=1;
for(int x=1;x<=n;++x)
for(int y=1;y<=n;++y){
if(b[x][y]||(x==1&&y==1)) continue;
f[x][y]=add(f[x-1][y],f[x][y-1]);
}
printf("%d\n",f[n][n]);
return 0;
}
T30 [USACO1.5][IOI1994]数字三角形 Number Triangles
IOI1994?
就这就这?
好像是第一道看的DP题
(当然当时很菜)
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=1e3+5;
int n,a[N][N],f[N][N],ans=-2147483647;
int main(){
n=in;
for(int i=1;i<=n;++i)
for(int j=1;j<=i;++j)
a[i][j]=in;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j){
f[i][j]=max(f[i-1][j],f[i-1][j-1])+a[i][j];
}
for(int i=1;i<=n;++i)
ans=max(ans,f[n][i]);
cout<<ans<<endl;
return 0;
}
T31 【模板】最长公共子序列
LCS板题
考虑维护
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示第一个数列第i位前和第二个数列第j位前的LCS
菜至极
只会
n
2
n^2
n2
考虑转化
这是个排列,就可以搞事情
把一个数列的数变成下标,相应的替换另一个数列里的数
这样不会改变两个数列LCS的本质
转化后发现只需要求另一个数列的LIS(最长上升子序列)了
LIS当然用单调队列优化
优秀✌️
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=1e5+5;
int n,a[N],b[N],q[N],len=1;
int main(){
n=in;
for(int i=1;i<=n;++i) a[in]=i;
for(int j=1;j<=n;++j) b[j]=a[in];
q[1]=b[1];
for(int i=1;i<=n;++i){
if(b[i]>q[len]){q[++len]=b[i];continue;}
int pos=lower_bound(q+1,q+len+1,b[i])-q;
q[pos]=min(q[pos],b[i]);
}
printf("%d\n",len);
return 0;
}
T32 友好城市
本来今天排到一道数位DP的
但是数位DP我自己准备了好多题
就咕了
过两天做专题
NOIP做过
双倍经验
双倍经验个鬼
后面50pts卡
n
2
n^2
n2
二分图呗
二分图个鬼
人家求的是航道不交叉qwq
发现:
两条航线起点终点都是小于或大于,航线不相交
对起点排序
求终点的最长不下降序列
单调队列优化
有点淦的是
不知道为什么记录单调队列长度siz
的历史最大值就不对qwq
求巨佬解释
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=2e5+5;
int n,f[N],q[N],siz=1,ans;
struct node{
int u,v;
}p[N];
bool cmp(const node a,const node b){
return a.u<b.u;
}
int main(){
n=in;
for(int i=1;i<=n;++i) p[i].u=in,p[i].v=in;
sort(p+1,p+n+1,cmp);
q[1]=p[1].v;
for(int i=2;i<=n;++i){
if(p[i].v>q[siz]){q[++siz]=p[i].v;continue;}
int pos=lower_bound(q+1,q+siz+1,p[i].v)-q;
q[pos]=min(q[pos],p[i].v);
}
printf("%d\n",siz);
return 0;
}
T33 旅行计划
单源单汇就是最长路
多源单汇就是反一下最长路
多源多汇就是拓扑排序
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=2e5+5;
int n,m,deg[N],f[N];
vector<int>G[N];
void topo(){
queue<int>q;
for(int i=1;i<=n;++i) if(!deg[i]) q.push(i),f[i]=1;
while(!q.empty()){
int u=q.front();q.pop();
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
--deg[v];
f[v]=max(f[v],f[u]+1);
if(!deg[v]) q.push(v);
}
}
return;
}
int main(){
n=in,m=in;
for(int i=1;i<=m;++i){
int u=in,v=in;
G[u].push_back(v);
++deg[v];
}
topo();
for(int i=1;i<=n;++i) printf("%d\n",f[i]);
return 0;
}
T34 [USACO09DEC]Dizzy Cows G
考察最后图的性质:DAG
联想到拓排
拓排要求DAG,如果在排的时候强行整成DAG呢
是可以的
于是按有向边拓排
遇到无向边将它改造为从这个点出发的有向边
一通暴搞下来得到的就是DAG
题目的
数据保证一开始就有的单向道路中
就很优秀
而且保证有解
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=2e5+5;
int n,m1,m2;
int tot,first[N],nxt[N],aim[N],wei[N],ori[N];
int deg[N];
queue<int>q;
void ljb(int u,int v,int w){
++tot;
nxt[tot]=first[u];
first[u]=tot;
aim[tot]=v;
ori[tot]=u;
wei[tot]=w;
return;
}
signed main(){
// freopen("IN.in","r",stdin);
// freopen("out.out","w",stdout);
n=in,m1=in,m2=in;
for(int i=1;i<=m1;++i){
int u=in,v=in;
ljb(u,v,0);
++deg[v];
}
for(int i=1;i<=n;++i) if(!deg[i]) q.push(i);
if(!(tot&1)) ++tot;
for(int i=1;i<=m2;++i){
int u=in,v=in;
ljb(u,v,1);
ljb(v,u,1);
}
while(!q.empty()){
int u=q.front();q.pop();
for(int e=first[u];e;e=nxt[e])
if(!wei[e]){
int v=aim[e];
--deg[v];
if(!deg[v]) q.push(v);
}
for(int e=first[u];e;e=nxt[e])
if(wei[e]==1) wei[e^1]=2;
}
for(int i=1;i<=n;++i) if(deg[i]){puts("-1");return 0;}
bool flag=false;
for(int i=1;i<=tot;++i)
if(wei[i]==1)
printf("%lld %lld\n",ori[i],aim[i]),flag=true;
if(!flag) puts("-1");
return 0;
}
这第10个点。。。
invalid answer
T35 [HAOI2016]食物链
不科学!
他没说食物链是随便一条链还是入度0
到出度0
的链!
设入度0
的点有
x
x
x个,总共
n
n
n个点
那么从每个入度进去遍历整张图
O
(
x
(
n
−
x
)
)
O(x(n-x))
O(x(n−x))
可能T掉
考虑记搜
记录每个点上能到达出度0
点的个数
这个值是一定的,可以记搜
优化至
O
(
n
)
O(n)
O(n)
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=2e5+5;
int n,m;
int tot,first[N],nxt[N],aim[N];
int indeg[N],outdeg[N],f[N];
void ljb(int u,int v){
++tot;
nxt[tot]=first[u];
first[u]=tot;
aim[tot]=v;
return;
}
int DFS(int u){
if(f[u]) return f[u];
if(!outdeg[u]) return 1;
int ans=0;
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
ans+=DFS(v);
}
return f[u]=ans;
}
int main(){
n=in,m=in;
for(int i=1;i<=m;++i){
int x=in,y=in;
ljb(x,y);
++indeg[y];
++outdeg[x];
}
int ans=0;
for(int i=1;i<=n;++i)
if(!indeg[i]&&outdeg[i])
ans+=DFS(i);
printf("%d\n",ans);
return 0;
}
T36 最大食物链计数
双倍经验
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=1e6+5;
const int mod=80112002;
int n,m;
int tot,first[N],nxt[N],aim[N];
int indeg[N],outdeg[N],f[N];
int add(int a,int b){return a+b>mod?a+b-mod:a+b;}
void ljb(int u,int v){
++tot;
nxt[tot]=first[u];
first[u]=tot;
aim[tot]=v;
return;
}
int DFS(int u){
if(f[u]) return f[u];
if(!outdeg[u]) return 1;
int ans=0;
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
ans=add(ans,DFS(v));
}
return f[u]=ans;
}
int main(){
n=in,m=in;
for(int i=1;i<=m;++i){
int x=in,y=in;
ljb(x,y);
++indeg[y];
++outdeg[x];
}
int ans=0;
for(int i=1;i<=n;++i)
if(!indeg[i]&&outdeg[i])
ans=add(ans,DFS(i));
printf("%d\n",ans);
return 0;
}
T37 绿豆蛙的归宿
期望概率DP
考虑
f
i
=
∑
(
p
[
i
→
j
]
f
[
j
]
+
w
[
i
→
j
]
)
f_i=\sum (p[i\to j]f[j]+w[i\to j])
fi=∑(p[i→j]f[j]+w[i→j])
对于一个点,从它的所有终点走过来
终点期望有累加,边权要考虑
f
u
=
∑
u
→
v
(
f
v
+
w
e
)
p
e
f_u=\sum_{u\to v} (f_v+w_e)p_e
fu=u→v∑(fv+we)pe
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=2e5+5;
int n,m;
int tot,first[N],nxt[N],aim[N],wei[N];
double f[N];
int ind[N],siz[N];
void ljb(int u,int v,int w){
++tot;
nxt[tot]=first[u];
first[u]=tot;
aim[tot]=v;
wei[tot]=w;
return;
}
void topo(){
queue<int>q;
q.push(n);
while(!q.empty()){
int u=q.front();q.pop();
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
f[v]+=(f[u]+1.0*wei[e])/siz[v];
if(!(--ind[v])) q.push(v);
}
}
return;
}
int main(){
n=in,m=in;
for(int i=1;i<=m;++i){
int u=in,v=in,w=in;
ljb(v,u,w);
++ind[u];
++siz[u];
}
topo();
printf("%.2lf\n",f[1]);
return 0;
}
T38 The least round way
2
×
5
=
10
;
x
⋅
0
=
0
2\times 5=10;x\cdot 0=0
2×5=10;x⋅0=0
情况1:统计2,5个数,
a
n
s
=
min
(
2
n
,
n
,
5
n
,
n
)
ans=\min(2_{n,n},5_{n,n})
ans=min(2n,n,5n,n);
情况2:找到0的路径,答案恒为1。
a
n
s
=
min
(
1
,
a
n
s
)
ans=\min(1,ans)
ans=min(1,ans)
输出路径考虑递归
情况2直接干
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=1e3+5,INF=2147483647;
int n,a[N][N][2],f[N][N][2],ans,_x,t;
void print(int x,int y,int k){
if(x==1&&y==1){putchar(k?'D':'R');return;}
if(x==1) print(x,y-1,0);
else if(y==1) print(x-1,y,1);
else if(f[x][y][t]==f[x][y-1][t]+a[x][y][t]) print(x,y-1,0);
else print(x-1,y,1);
if(x!=n||y!=n){putchar(k?'D':'R');return;}
}
int main(){
n=in;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j){
int x=in;
if(!x) _x=i;
else{
while(!(x&1)) ++a[i][j][0],x>>=1;
while(!(x%5)) ++a[i][j][1],x/=5;
}
}
for(int i=1;i<=n;++i)
f[0][i][0]=f[0][i][1]=f[i][0][0]=f[i][0][1]=INF;
f[1][1][0]=a[1][1][0];
f[1][1][1]=a[1][1][1];
for(int k=0;k<=1;++k)
for(int i=1;i<=n;++i)
for(int j=(i==1?2:1);j<=n;++j)
f[i][j][k]=min(f[i-1][j][k],f[i][j-1][k])+a[i][j][k];
ans=min(f[n][n][0],f[n][n][1]);
if(_x&&ans>1){
puts("1");
for(int i=1;i<_x;++i) putchar('D');
for(int i=1;i<n;++i) putchar('R');
for(int i=_x;i<n;++i) putchar('D');
}else{
printf("%d\n",ans);
t=!(f[n][n][0]<f[n][n][1]);
print(n,n,0);
}
puts("");
return 0;
}
T39 Writing Code
500
500
500的数据范围就很奇怪
大概是个三次算法
一看题,像个背包
n
n
n种物品,每种无限个,两种体积:
第一种每个物品体积1,容积
m
m
m,需完全;
第二种每个物品体积
a
i
a_i
ai,容积
b
b
b,不需完全
O
(
n
m
b
)
O(nmb)
O(nmb)
注意累计方案数和求最大价值有区别
最大价值是求max,方案数是累加
最终输出的不是出错b个的,而是出错b个以下的
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=505;
int n,m,b,a[N],mod;
int f[N][N];
int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
int main(){
n=in,m=in,b=in,mod=in;
for(int i=1;i<=n;++i) a[i]=in;
f[0][0]=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
for(int k=a[i];k<=b;++k)
f[j][k]=add(f[j][k],f[j-1][k-a[i]]);
int ans=0;
for(int i=0;i<=b;++i) ans=add(ans,f[m][i]);
printf("%d\n",ans);
return 0;
}
T40 尼克的任务
倒着加
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=1e5+5;
int n,m,sum[N],f[N],cnt=1;
struct Data{
int p,t;
}d[N];
bool cmp(const Data &u,const Data &v){return u.p>v.p;}
int main(){
n=in,m=in;
for(int i=1;i<=m;++i){
d[i].p=in,d[i].t=in;
++sum[d[i].p];
}
sort(d+1,d+m+1,cmp);
for(int i=n;i>=1;--i){
if(!sum[i]) f[i]=f[i+1]+1;
else for(int j=1;j<=sum[i];++j){
if(f[i]<f[i+d[cnt].t])
f[i]=f[i+d[cnt].t];
++cnt;
}
}
printf("%d\n",f[1]);
return 0;
}
T41 字串距离
两个东西
考虑
f
[
i
]
[
j
]
f[i][j]
f[i][j]直接递推
注意预处理:全是空格的情况
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=3e3+5;
char a[N],b[N];
int k,la,lb,f[N][N];
int main(){
scanf("%s%s",a+1,b+1);
k=in;
la=strlen(a+1),lb=strlen(b+1);
for(int i=1;i<=la;++i) f[i][0]=f[i-1][0]+k;
for(int j=1;j<=lb;++j) f[0][j]=f[0][j-1]+k;
for(int i=1;i<=la;++i)
for(int j=1;j<=lb;++j){
f[i][j]=min(min(f[i-1][j]+k,f[i][j-1]+k),f[i-1][j-1]+abs(a[i]-b[j]));
}
printf("%d\n",f[la][lb]);
return 0;
}
T42 NAPTIME - Naptime
f
[
i
]
[
j
]
[
0
/
1
]
f[i][j][0/1]
f[i][j][0/1]表示第
i
i
i小时,睡了
j
j
j小时,现在在/不在睡觉的最优解
第一遍:前天晚上没睡着
f
[
i
]
[
j
]
[
0
]
=
max
(
f
[
i
−
1
]
[
j
]
[
0
]
,
f
[
i
−
1
]
[
j
]
[
1
]
)
f
[
i
]
[
j
]
[
1
]
=
max
(
f
[
i
−
1
]
[
j
−
1
]
[
0
]
,
f
[
i
−
1
]
[
j
−
1
]
[
1
]
+
u
[
i
]
)
\begin{aligned} &f[i][j][0]=\max(f[i-1][j][0],f[i-1][j][1])\\ &f[i][j][1]=\max(f[i-1][j-1][0],f[i-1][j-1][1]+u[i]) \end{aligned}
f[i][j][0]=max(f[i−1][j][0],f[i−1][j][1])f[i][j][1]=max(f[i−1][j−1][0],f[i−1][j−1][1]+u[i])
边界为0
a
n
s
1
=
max
(
f
[
n
]
[
b
]
[
0
]
,
f
[
n
]
[
b
]
[
1
]
)
ans_1=\max(f[n][b][0],f[n][b][1])
ans1=max(f[n][b][0],f[n][b][1])
第二遍:前天晚上睡着了
f
[
1
]
[
1
]
[
1
]
=
u
[
1
]
f[1][1][1]=u[1]
f[1][1][1]=u[1]
再做一遍DP
a
n
s
=
max
(
a
n
s
1
,
f
[
n
]
[
b
]
[
1
]
)
ans=\max(ans_1,f[n][b][1])
ans=max(ans1,f[n][b][1])
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=5e3+5;
int T,n,b,u[N],f[N][N][2],ans;
void DP(){
for(int i=1;i<=n;++i)
for(int j=1;j<=b;++j){
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
if(j!=1)
f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+u[i]);
}
return;
}
void solve(){
n=in,b=in;
for(int i=1;i<=n;++i) u[i]=in;
DP();
ans=max(f[n][b][0],f[n][b][1]);
f[1][1][1]=u[1];
DP();
ans=max(ans,f[n][b][1]);
printf("%d\n",ans);
return;
}
int main(){
T=in;
while(T--) solve();
return 0;
}