这题真心神坑,刚拿到这个题,想了一会,感觉可以用差分约束做,写完了之后稍微调了一下,交上去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]); } } } }