题目
原题链接
出题人:博主(DarthVictor) 造数据:(zhl) 协助:(gyz)
现属于洛谷本团队题库。
解说
五一假期组织了一起三个小组互相出题互相做的活动,每组两道。(gyz)大佬自己扛下一道(链接),我想出一道图论,就开始往那个方面想,最后先想出来的版本是没有秒杀武器的,想的标准思路是把有权值的点拆点来解决,但题目提出之后很快(gyz)大佬就提出了不拆点的思路。所以还是太简单了,得想办法再加难度……后来想到了原来做过的一道冻结,就想到要不加上一个秒杀武器,这样既引入了分层图还能强迫做的人拆点……
然后就有了这个东西。由于本人蒟蒻,知识面浅薄,本题的数据由(zhl)大佬制作。
下面就说一下我的标程的思路:
普通的点就直接建双层图,传送门不过是在两个点之间建一条权值为(0)的路而已。遇到有权值的点,就拆为两个点,一个点连所有入边,另一个连所有出边,之后连入点再连一条权值为(0)的路到第二层的出点,这样经过有权值的点就必须过拆开的点之间的路,如果使用武器就可以不花费地抵达第二层,之后就没法再次使用。这样以后再跑一遍最短路,取第一层终点和第二层终点距离的最小值即可(注意取最小值)。
之后又是(gyz)想出来其他做法:不拆点,把通往第二层的梯子的权值设为点权的相反数,这样上第二层就能抵消点权,之后用(SPFA)跑最短路即可。
本来我们想卡掉非标程的做法,就想造一个卡(SPFA)的图,结果发现即便是这样的图(SPFA)也并不慢,毕竟不用拆点,边比较少。所以我觉得既然这也是一种思路,那就不卡了。
写的时候代码的细节还是挺多的。因为各种细节,我的标程卡了两天才改完所有错误……
代码
标程代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1200000+5,maxe=10000000+5,INF=0x3f3f3f3f;
int n,m,x,y,tot,dy[maxn],dis[maxn],head[maxn];
bool vis[maxn];
struct edge{
int to,next,w;
}e[maxn];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
struct cun{
int a,b,l;
}temp[maxe];
void add(int a,int b,int l){
e[tot].w=l;
e[tot].to=b;
e[tot].next=head[a];
head[a]=tot;
tot++;
}
struct Node{
int id,val;
Node(){}
Node(int a,int b){
id=a;val=b;
}
bool operator < (const Node&A)const{
return val>A.val;
}
};
void Dijs(){
memset(dis,0x3f,sizeof(dis));
priority_queue<Node> q;
dis[1]=0;
q.push(Node(1,0));
while(!q.empty()){
Node u=q.top();q.pop();
if(vis[u.id])continue;
vis[u.id]=1;
for(int i=head[u.id];i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(dis[v]>dis[u.id]+w){
dis[v]=dis[u.id]+w;
q.push(Node(v,dis[v]));
}
}
}
}
int main(){
tot=1;
n=read(); m=read(); x=read(); y=read();
for(int i=1;i<=m;i++){
temp[i].a=read();
temp[i].b=read();
temp[i].l=read();
}
for(int i=1;i<=x;i++){
int u,w;
u=read(); w=read();
dy[u]=n+i;
add(u,dy[u],w);
add(u+n+x+1,dy[u]+n+x+1,w);
add(u,dy[u]+n+x+1,0);
}
for(int i=1;i<=m;i++){
if(dy[temp[i].a]){
add(dy[temp[i].a],temp[i].b,temp[i].l);
add(dy[temp[i].a]+n+x+1,temp[i].b+n+x+1,temp[i].l);
}
else{
add(temp[i].a,temp[i].b,temp[i].l);
add(temp[i].a+n+x+1,temp[i].b+n+x+1,temp[i].l);
}
}
for(int i=1;i<=y;i++){
int a,b;
a=read(); b=read();
if(dy[a]){
add(dy[a],b,0);
add(dy[a]+n+x+1,b+n+x+1,0);
}
else{
add(a,b,0);
add(a+n+x+1,b+n+x+1,0);
}
}
Dijs();
if(dis[n+n+x+1]==INF&&dis[n]==INF) printf("-1");
else printf("%d",min(dis[n+n+x+1],dis[n]));
return 0;
}
标程·改
#include<bits/stdc++.h>
using namespace std;
const int maxn=1200000+5,maxe=10000000+5,INF=0x3f3f3f3f;
int n,m,x,y,tot,dy[maxn],dis[maxn],head[maxn];
bool vis[maxn];
struct edge{
int to,next,w;
}e[maxn];
struct cun{
int a,b,l;
}temp[maxe];
void add(int a,int b,int l){
e[tot].w=l;
e[tot].to=b;
e[tot].next=head[a];
head[a]=tot;
tot++;
}
int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
void Spfa(int begin){
deque<int> q;
memset(dis,0x3f,sizeof(dis));
dis[begin]=0,vis[begin]=1;
q.push_back(begin);
while(!q.empty()){
int u=q.front();
q.pop_front();
vis[u]=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
if(!vis[v]){
if(!q.empty()&&dis[v]>=dis[q.front()]) q.push_back(v);
else q.push_front(v);
vis[v]=1;
}
}
}
}
}
int main(){
tot=1;
n=read(); m=read(); x=read(); y=read();
for(int i=1;i<=m;i++){
temp[i].a=read();
temp[i].b=read();
temp[i].l=read();
}
for(int i=1;i<=x;i++){
int u,w;
u=read(); w=read();
dy[u]=n+i;
add(u,dy[u],w);
add(u+n+x+1,dy[u]+n+x+1,w);
add(u,dy[u]+n+x+1,0);
}
for(int i=1;i<=m;i++){
if(dy[temp[i].a]){
add(dy[temp[i].a],temp[i].b,temp[i].l);
add(dy[temp[i].a]+n+x+1,temp[i].b+n+x+1,temp[i].l);
}
else{
add(temp[i].a,temp[i].b,temp[i].l);
add(temp[i].a+n+x+1,temp[i].b+n+x+1,temp[i].l);
}
}
for(int i=1;i<=y;i++){
int a,b;
a=read(); b=read();
if(dy[a]){
add(dy[a],b,0);
add(dy[a]+n+x+1,b+n+x+1,0);
}
else{
add(a,b,0);
add(a+n+x+1,b+n+x+1,0);
}
}
Spfa(1);
if(dis[n+n+x+1]==INF&&dis[n]==INF) printf("-1");
else printf("%d",min(dis[n+n+x+1],dis[n]));
return 0;
}
gjk大佬AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
inline long long read(){
long long x = 0, w = 1;
char ch = getchar();
for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10ll + ch - '0';
return x * w;
}
const int maxn = 1000010;
int tot, head[maxn*2];
struct node{
int to, nxt;
long long w;
}edge[maxn << 2];
inline void add(int x, int y, long long z){
edge[++tot].to = y;
edge[tot].nxt = head[x];
edge[tot].w = z;
head[x] = tot;
}
long long dis[maxn*2];
bool vis[maxn*2];
int val[maxn*2];
inline void spfa(int x){
memset(dis,0x3f,sizeof(dis));
queue<int> q;
q.push(x);
dis[x] = 0;
vis[x] = 1;
while(!q.empty()){
int now=q.front();
q.pop();
vis[now] = 0;
for(int i = head[now]; i; i = edge[i].nxt){
int v = edge[i].to;
if(dis[v] > dis[now] + edge[i].w){
dis[v] = dis[now] + edge[i].w;
if(!vis[v]){
vis[v] = 1;
q.push(v);
}
}
}
}
}
int main(){
int n = read(), m = read(), x = read(), y = read();
for(int i = 1; i <= m; i++){
int u = read(), v = read(), w = read();
add(u, v, w);
add(u+n, v+n, w);
}
for(int i = 1; i <= x; i++){
int u = read();
val[u] = read();
val[u+n]=val[u];
}
for(int i = 1; i <= y; i++){
int u = read(), v = read();
add(u, v, 0);
add(u+n, v+n, 0);
}
for(int i=1;i<=2*n;i++)
for(int j=head[i];j;j=edge[j].nxt){
add(i,edge[j].to+n,edge[j].w);
edge[j].w+=val[edge[j].to];
}
spfa(1);
if(dis[n]>=1e17)
printf("-1");
else
cout << min(dis[n],dis[2*n]) << endl;
return 0;
}
lc大佬AC代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int maxd=300005,maxb=3301005;
int head[maxd],tot=1;
struct asd{
int to,next,val;
}b[maxb];
int jla[maxb],jlb[maxb],jlc[maxb];
inline void ad(int aa,int bb,int cc){
b[tot].to=bb;
b[tot].next=head[aa];
b[tot].val=cc;
head[aa]=tot++;
}
int diss[maxd];
int n,m,x,y;
struct jie{
int num,dis,kk;
jie(int aa=0,int bb=0,int cc=0){
num=aa,dis=bb,kk=cc;
}
bool operator < (const jie &A) const{
return dis>A.dis;
}
};
priority_queue<jie> q;
int dis[maxd][2];
bool vis[maxd][2];
void solve(){
memset(dis,0x3f,sizeof(dis));
q.push(jie(1,0,0));
dis[1][0]=0;
while(!q.empty()){
jie xx=q.top();
q.pop();
if(vis[xx.num][xx.kk]==true) continue;
vis[xx.num][xx.kk]=true;
for(int i=head[xx.num];i!=-1;i=b[i].next){
int u=b[i].to;
if(dis[u][xx.kk]>dis[xx.num][xx.kk]+b[i].val){
dis[u][xx.kk]=dis[xx.num][xx.kk]+b[i].val;
q.push(jie(u,dis[u][xx.kk],xx.kk));
}
if(xx.kk<1 && dis[u][xx.kk+1]>dis[xx.num][xx.kk]+b[i].val-diss[u]){
dis[u][xx.kk+1]=dis[xx.num][xx.kk]+b[i].val-diss[u];
q.push(jie(u,dis[u][xx.kk+1],xx.kk+1));
}
}
}
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d%d%d",&n,&m,&x,&y);
for(int i=1;i<=m;i++){
int aa,bb,cc;
scanf("%d%d%d",&aa,&bb,&cc);
jla[i]=aa,jlb[i]=bb,jlc[i]=cc;
}
for(int i=1;i<=x;i++){
int aa,bb;
scanf("%d%d",&aa,&bb);
diss[aa]=bb;
}
for(int i=1;i<=y;i++){
int aa,bb;
scanf("%d%d",&aa,&bb);
jla[m+i]=aa,jlb[m+i]=bb,jlc[m+i]=0;
}
for(int i=1;i<=m+y;i++){
ad(jla[i],jlb[i],jlc[i]+diss[jlb[i]]);
}
int ans=0x3f3f3f3f;
solve();
ans=min(dis[n][0],dis[n][1]);
if(ans==0x3f3f3f3f) printf("-1
");
else printf("%d
",ans);
return 0;
}
尾声
几种版本的代码都附在上面了供大家研究。
对比一下:
这次出题真的是感觉出题比做题难……要考虑的太多了,可能会有什么水题做法,怎么能卡掉,设置多长的时间合适……真的恶心到了……
还有@gjk说我们题简单您倒是一遍A掉啊WA了5遍就不要喷人家啊略略略……
幸甚至哉,歌以咏志。