这题真心神坑,刚拿到这个题,想了一会,感觉可以用差分约束做,写完了之后稍微调了一下,交上去WA了,改了一会也还是WA,以为是算法错了。然后发现是有规律的,又改用数学方法去做,写的有些挫,出数据调了不少bug,但是还是WA到死,最后也没做出来。今天又想了一下,队友发现每个点取值的范围是非负,而不是0到10000,不得不吐槽题意真心坑,然后把之前的两个代码都去掉10000的约束以后都过了。读题是硬伤啊……
差分约束:分别维护前i个人拥有石头个数的最大可能值和最小可能值,第i个人可能的最大数值就是dismax[i]-dismin[i-1]
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
#define MAXN 100005
int dismax[MAXN],dismin[MAXN];
int vis[MAXN];
const int INF=0x3f3f3f3f;
struct Node
{
int v,w;
Node(int a,int b){v=a,w=b;}
};
int n;
vector<Node> gmin[MAXN],gmax[MAXN];
void spfamax()
{
for(int i=0;i<=n;i++)
dismax[i]=INF;
memset(vis,0,sizeof(vis));
dismax[0]=0;
queue<int> q;
q.push(0);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=false;
int len=gmax[u].size();
for(int i=0;i<len;i++)
{
int v=gmax[u][i].v,w=gmax[u][i].w;
if(dismax[v]>dismax[u]+w)
{
dismax[v]=dismax[u]+w;
if(!vis[v])
{
vis[v]=true;
q.push(v);
}
}
}
}
}
void spfamin()
{
for(int i=0;i<=n;i++)
dismin[i]=-INF;
memset(vis,0,sizeof(vis));
dismin[0]=0;
queue<int> q;
q.push(0);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=false;
int len=gmin[u].size();
for(int i=0;i<len;i++)
{
int v=gmin[u][i].v,w=gmin[u][i].w;
if(dismin[v]<dismin[u]+w)
{
dismin[v]=dismin[u]+w;
if(!vis[v])
{
vis[v]=true;
q.push(v);
}
}
}
}
}
int main()
{
//freopen("test.txt","r",stdin);
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<=n;i++)
gmin[i].clear(),gmax[i].clear();
for(int i=1;i<=n;i++)
{
gmax[i].push_back(Node(i-1,0));
gmin[i-1].push_back(Node(i,0));
}
for(int i=1;i<=n;i++)
{
int temp;
scanf("%d",&temp);
if(temp!=-1)
{
gmax[i-1].push_back(Node(i,temp));
gmax[i].push_back(Node(i-1,-temp));
gmin[i-1].push_back(Node(i,temp));
gmin[i].push_back(Node(i-1,-temp));
}
}
for(int i=1;i<=n;i++)
{
int w;
scanf("%d",&w);
int u=max(0,i-2),v=min(n,i+1);
gmax[u].push_back(Node(v,w));
gmax[v].push_back(Node(u,-w));
gmin[u].push_back(Node(v,w));
gmin[v].push_back(Node(u,-w));
}
spfamax();
spfamin();
int q;
scanf("%d",&q);
for(int i=0;i<q;i++)
{
int temp;
scanf("%d",&temp);
temp++;
printf("%d\n",dismax[temp]-dismin[temp-1]);
}
}
}
数学规律:3,6,9,……都可以算出来,然后第1项可以推4,7,10……,第2项可以推5,8,11……,前两项是固定的,设第一项为x,第二项为num[1]-x,add维护
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define MAXN 100005
int num[MAXN];
int tot[MAXN];
int value[MAXN];
int add[MAXN];
int main()
{
//freopen("test.txt","r",stdin);
int n;
while(scanf("%d",&n)!=EOF)
{
memset(value,0,sizeof(value));
memset(add,0,sizeof(add));
for(int i=1; i<=n; i++)
scanf("%d",&tot[i]);
for(int i=1; i<=n; i++)
scanf("%d",&num[i]);
int l=0,r=num[1],addx=0,addy=0;
bool ok=false;
for(int i=1; i<=n; i++)
{
if(i%3==1)
{
if(i>3)
addx+=num[i-1]-num[i-2];
if(tot[i]!=-1)
{
value[1]=tot[i]-addx;
value[2]=num[1]-value[1];
ok=true;
}
else
{
//r=min(r,10000-addx);
l=max(l,-addx);
}
add[i]=addx;
}
else if(i%3==2)
{
if(i>3)
addy+=num[i-1]-num[i-2];
if(tot[i]!=-1)
{
value[1]=num[1]+addy-tot[i];
value[2]=num[1]-value[1];
ok=true;
}
else
{
r=min(r,num[1]+addy);
//l=max(l,num[1]+addy-10000);
}
add[i]=addy;
}
else
{
value[i]=value[i-3]+num[i-1]-num[i-2];
}
}
if((n-2)%3!=0)
{
ok=true;
if((n-2)%3==1)
{
value[1]=num[n-1]-num[n]-add[n-2];
value[2]=num[1]-value[1];
}
else
{
value[1]=num[1]+add[n-2]-(num[n-1]-num[n]);
value[2]=num[1]-value[1];
}
}
int q;
scanf("%d",&q);
for(int i=0;i<q;i++)
{
int temp;
scanf("%d",&temp);
if((temp+1)%3==0)
printf("%d\n",value[temp+1]);
else if(ok)
{
printf("%d\n",value[(temp+1)%3]+add[temp+1]);
}
else
{
if((temp+1)%3==1)
printf("%d\n",r+add[temp+1]);
else printf("%d\n",num[1]-l+add[temp+1]);
}
}
}
}