bzoj 4289 TAX —— 点边转化
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4289
把边转化成点,同一个原有点相连的边中,边权小的向大的连差值的边,大的向小的连0的边;
一开始想的是给每个新点记一个点权是这个点(边)原来的权,走到它时先加上点权,因为要在原图上经过这条边还是要花费边权;
但是这样在原图中的边之间转移时会把它们的边权都加上,就不对了;
所以应该是把原图的边进一步拆成两个点,两端点的集合各加入一个,这两点之间连原边权的边;
题目上什么也没说...总之 dis 是 long long 的;
如果直接把刚才存边用的结构体放进 queue,就要注意把结构体的 w 定义成 ll,而且一定注意结构体内部定义变量的顺序!因为 (N){..., ...} 的写法是强调顺序的!
——然后发现这道题三个月前做过-_- https://www.cnblogs.com/Zinn/p/9326302.html
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> #define pb push_back using namespace std; typedef long long ll; int const xn=2e5+5,xm=1e5+5; int n,m,s,t,hd[xn<<1],ct,to[xn<<3],nxt[xn<<3],w[xn<<3]; ll dis[xn<<1]; bool vis[xn<<1]; struct N{ ll w; int id;//ll(dis) bool operator < (const N &y) const {return w<y.w;} }; vector<N>v[xm]; priority_queue<N>q; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return f?ret:-ret; } void add(int x,int y,int z){to[++ct]=y; nxt[ct]=hd[x]; w[ct]=z; hd[x]=ct;} void dij() { while(q.size()) { int x=q.top().id; q.pop(); if(vis[x])continue; vis[x]=1; for(int i=hd[x],u;i;i=nxt[i]) { if(dis[u=to[i]]>dis[x]+w[i]) dis[u]=dis[x]+w[i],q.push((N){-dis[u],u}); } } } int main() { n=rd(); m=rd(); s=0; t=2*m+1; for(int i=1,x,y,z;i<=m;i++) { x=rd(); y=rd(); z=rd(); v[x].pb((N){z,i}); v[y].pb((N){z,i+m}); add(i,i+m,z); add(i+m,i,z); } for(int i=0;i<v[1].size();i++)add(s,v[1][i].id,v[1][i].w); for(int i=0;i<v[n].size();i++)add(v[n][i].id,t,0);//0 for(int i=1;i<=n;i++) { sort(v[i].begin(),v[i].end()); for(int j=1;j<v[i].size();j++) add(v[i][j-1].id,v[i][j].id,v[i][j].w-v[i][j-1].w),add(v[i][j].id,v[i][j-1].id,0); } memset(dis,0x3f,sizeof dis); dis[s]=0; q.push((N){0,s}); dij(); printf("%lld\n",dis[t]); return 0; }