代码源每日一题
103 子串最大差
题目描述
题解
每个区间会有一个最大值和最小值,最大差是最大值减最小值。我们需要将所有的子串的最大差累加起来。将问题转化一下,可以单独计算每个a[i]作为最大值的贡献以及最小值的贡献。而a[i]作为最大值的贡献就是包含它的所有满足它是最大值的字串。注意到,从a[i]向左一直走,找到能维持a[i]是最大值的最左端,向右一直走,找到能维持a[i]最大值的最右端,那么满足条件的子串数就是左边的长度乘右边的长度。这个东西可以通过二分+ST表实现,ST表 NlogN预处理以后,查询O(1)。整体复杂度就是NlogN。或者直接写一个线段树二分也可以。但所求的东西可以很方便的用单调栈维护。易错点是,如果两遍单调栈维护的都是非严格单调序列,那么一个子串可能会被重复计数,比如3 2 2 3这个子串会被两个3分别计算一次。因此需要一遍是非严格,一遍是严格。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e5+10;
int n,a[N],lef[N],rig[N];
int s[N],top;
ll ans;
int main(){
// freopen("1.in","r",stdin);
// freopen("2.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
while(top&&a[s[top]]<=a[i]) top--;
lef[i]=i-s[top];
s[++top]=i;
}
top=0;s[0]=n+1;
for(int i=n;i;i--){
while(top&&a[s[top]]<a[i]) top--;
rig[i]=s[top]-i;
s[++top]=i;
}
for(int i=1;i<=n;i++){
//cout<<lef[i]<<' '<<rig[i]<<endl;
ans+=1LL*a[i]*lef[i]*rig[i];
}
top=0;s[0]=0;
for(int i=1;i<=n;i++){
while(top&&a[s[top]]>=a[i]) top--;
lef[i]=i-s[top];
s[++top]=i;
}
top=0;s[0]=n+1;
for(int i=n;i;i--){
while(top&&a[s[top]]>a[i]) top--;
rig[i]=s[top]-i;
s[++top]=i;
}
for(int i=1;i<=n;i++){
ans-=1LL*a[i]*lef[i]*rig[i];
}
cout<<ans<<endl;
return 0;
}
104 no crossing
题目描述
题解
如图,按照要求,我们只能这样走,每次走完之后,之前所在的点就相当于障碍物,无法跨过。倘若我们走完之后,把整条路径反过来看:
对应下来就变为,倘若当前最后一步的端点是x,y,那么下一步要走的点必须在两个端点的外面。这样反过来的目的是为了方便递推。考虑区间dp,f[i][j][k][0/1]表示走了k步,现在经过的点的左端点是i,右端点是j,现在在i或者j时的最短路。直接递推即可。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int f[N][N][N][2];//f i j k 0/1表示当前左端点在l,右端点在r,经过了k个节点,现在在左/右的最小值
int n,K,m;
int dis[N][N];
int main(){
memset(f,0x3f,sizeof(f));
memset(dis,0x3f,sizeof(dis));
scanf("%d%d%d",&n,&K,&m);
for(int i=1;i<=m;i++){
int x,y,z;scanf("%d%d%d",&x,&y,&z);
int c=x;x=y;y=c;
dis[x][y]=min(dis[x][y],z);
if(x<y){
f[x][y][2][1]=min(f[x][y][2][1],z);
}else{
f[y][x][2][0]=min(f[y][x][2][0],z);
}
}
for(int k=2;k<K;k++){
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
for(int l=1;l<i;l++){
f[l][j][k+1][0]=min(f[l][j][k+1][0],f[i][j][k][0]+dis[i][l]);
f[l][j][k+1][0]=min(f[l][j][k+1][0],f[i][j][k][1]+dis[j][l]);
}
for(int r=j+1;r<=n;r++){
f[i][r][k+1][1]=min(f[i][r][k+1][1],f[i][j][k][0]+dis[i][r]);
f[i][r][k+1][1]=min(f[i][r][k+1][1],f[i][j][k][1]+dis[j][r]);
}
}
}
}
int ans=1e7;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
ans=min(ans,f[i][j][K][0]);
ans=min(ans,f[i][j][K][1]);
}
}
if(K==1){
cout<<0<<endl;
return 0;
}
if(ans==1e7){
cout<<-1<<endl;
}else{
cout<<ans<<endl;
}
return 0;
}