题目链接:zoj 4028
最长上升子串问题,逆向思考一下。给出每个dp【i】的值和每个数字的上下界,求一个合法的序列
考虑三点:
①根据每个数的上下界l<=ai<=ri有(1)a[i]<=0+r
(2) 0<=a[i]+(-l)
②根据上一个和a【i】相同数量级的数字b【a【i】】dis[a[i]]<=dis[b[a[i]]]+0
③根据上一个比a【i】小1的数字b【a【i】-1】dis[a[i]]<=dis[b[a[i]-1]]+1
由这三条规则建边然后spfa
#include<iostream>
#include<cstdio>
#include<vector>
#include<set>
#include<map>
#include<string.h>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#define LL long long
#define mod 1000000007
#define inf 0x7f7f7f7f
#define sqr(a) (a)*(a)
#define For(i,m,n) for(int i=m;i<=n;i++)
#define Dor(i,n,m) for(int i=n;i>=m;i--)
#define lan(a,b) memset(a,b,sizeof(a))
using namespace std;
struct node
{
int to;
int w;
int next;
};
node bian[500010*4];
int head[500010];
int a[500010];
int b[500010];
LL dis[500010];
int vis[500010];//判负环
int cnt;
int n;
void add(int p,int q,int w)
{
bian[cnt].to=q;
bian[cnt].w=w;
bian[cnt].next=head[p];
head[p]=cnt;
cnt++;
}
bool spfa()
{
queue<int> run;
run.push(0);
dis[0]=0;
vis[0]=1;
while(!run.empty())
{
int tem=run.front();
run.pop();
vis[tem]=0;
//cout << "tem=" << tem << endl;
for(int j=head[tem];~j;j=bian[j].next)
{
int to=bian[j].to;
int w=bian[j].w;
if(dis[to]>dis[tem]+w)
{
dis[to]=dis[tem]+w;
if(vis[to]==0)
run.push(to),vis[to]=1;
// printf("dis%d=%lld\n",to,dis[to]);
}
}
}
return true;
}
void init() {
for(int i = 0; i <= n; i++) {
dis[i] = inf;
vis[i] = 0;
head[i] = -1;
b[i]=0;
}
cnt = 0;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
cnt=0;
scanf("%d",&n);
init();
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
int p,q;
int tem=b[a[i]-1];
scanf("%d%d",&p,&q);
add(i,0,-p);
add(0,i,q);
if(b[a[i]])
add(b[a[i]],i,0);
if(a[i]>1)
add(i,tem,-1);
b[a[i]]=i;
}
spfa();
for(int i=1;i<=n;i++)
{
printf("%lld",dis[i]);
if(i==n)
printf("\n");
else
printf(" ");
}
}
return 0;
}