AcWing 362. 区间
差分约束系统班子;
注意:边多开几倍;边的方向;spfa与Dijkstra的区别;
if (d[y] < d[x] + Leng[i]) {
d[y] = d[x] + Leng[i];
if (!v[y]) {
v[y] = 1;
q.push(y);
}
}
单元最长路; d [ ] d[] d[]初始化为 0 x 3 f 3 f 3 f 3 f 0x3f3f3f3f 0x3f3f3f3f
#include<bits/stdc++.h>
using namespace std;
inline long long read(){
long long num=0;int z=1;char c=getchar();
if(c=='-') z=-1;
while((c<'0'||c>'9')&&c!='-') c=getchar();
if(c=='-') z=-1,c=getchar();
while(c>='0'&&c<='9') num=(num<<1)+(num<<3)+(c^48),c=getchar();
return z*num;
}
int n,a,b,c;
int maxx,minn,tot;
const int M=50005;
int d[3*M],v[3*M];
int head[3*M];
struct edge{
int to,w,next;
}e[3*M];
void add(int x,int y,int z)
{
tot++;e[tot].next=head[x]; e[tot].to=y; e[tot].w=z; head[x]=tot;
}
void spfa(int a)
{
queue<int>q;
d[a]=0;
v[a]=1;
q.push(minn);
while(!q.empty())
{
int x=q.front();
q.pop();
v[x]=0;
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if(d[y]<d[x]+e[i].w)
{
d[y]=d[x]+e[i].w;
if(!v[y])
{
q.push(y);
v[y]=1;
}
}
}
}
}
int main(){
n=read();
maxx=0;
minn=0x3f3f3f3f;
memset(d,0xcf,sizeof(d));
for(int i=1;i<=n;i++)
{
a=read();b=read();c=read();
add(a-1,b,c);
maxx=max(maxx,b);
minn=min(minn,a-1);
}
for(int i=minn;i<=maxx;i++)
{
add(i,i+1,0);
add(i+1,i,-1);
}
spfa(minn);
printf("%d",d[maxx]);
return 0;
}
[HNOI2005]狡猾的商人
判负环
#include<bits/stdc++.h>
using namespace std;
struct{
int nxt,mark,v;
}pre[2010];
int n,m,cnt;
bool used[110];
int dis[110];
int head[110];
int in[110];
int visited[110];
int t;
void add (int x,int y,int v,int w)
{
pre[w].mark=head[x];
pre[w].nxt=y;
pre[w].v=v;
head[x]=w;
}
bool check ()
{
queue<int>q;
for (int i=0;i<=n;i++)
if (in[i]==0)
{
q.push (i);
used[i]=1;
}
while (!q.empty())
{
int nw=q.front();
visited[nw]++;
if (visited[nw]>=n)
return 0;
for (int i=head[nw];i!=0;i=pre[i].mark)
{
int nx=pre[i].nxt;
int l=pre[i].v;
if (dis[nx]>dis[nw]+l)
{
dis[nx]=dis[nw]+l;
if (!used[nx])
{
q.push(nx);
used[nx]=1;
}
}
}
used[nw]=0;
q.pop();
}
return 1;
}
int main()
{
scanf ("%d",&t);
while (t--)
{
memset (in,0,sizeof (in));
memset (head,0,sizeof (head));
memset (used,0,sizeof (used));
memset (dis,0,sizeof (dis));
memset (visited,0,sizeof (visited));
scanf ("%d%d",&n,&m);
cnt=0;
for (int i=1;i<=m;i++)
{
int s,e,v;
scanf ("%d%d%d",&s,&e,&v);
add (s-1,e,-v,++cnt);
add (e,s-1,v,++cnt);
}
if (check ())
printf ("true\n");
else
printf ("false\n");
}
}
糖果
Solution:
巨说是一道缩点+拓扑排序的随便过的题。
然而菜鸡我只能打打思路简单点的差分约束了,重点是如何根据大小关系去建图。
我们不妨假设边 u → v u\rightarrow v u→v表示的是 v v v比 u u u大多少,贪心的想到要使得最后的糖果数最小,就尽可能的使得相连的两点糖果数差值尽可能的小(一定是以两者间小的为标准,相等时差为 0 0 0,否则大的数比小的至少大 1 1 1),最后的糖果总数显然最大。
于是我们针对这 5 5 5种情况分别建边(以下出现的 s i z [ x ] siz[x] siz[x]表示的是 x x x的糖果数):
1、当条件为 s i z [ u ] = = s i z [ v ] siz[u]==siz[v] siz[u]==siz[v],则建边 w [ u , v ] = 0 , w [ v , u ] = 0 w[u,v]=0,\;w[v,u]=0 w[u,v]=0,w[v,u]=0(表示 s i z [ u ] = = s i z [ v ] siz[u]==siz[v] siz[u]==siz[v])
2、当条件为 s i z [ u ] < s i z [ v ] siz[u]<siz[v] siz[u]<siz[v],若 u = = v u==v u==v则直接输出 − 1 -1 −1(显然不成立),否则建边 w [ u , v ] = 1 w[u,v]=1 w[u,v]=1(表示 s i z [ v ] siz[v] siz[v]比 s i z [ u ] siz[u] siz[u]大 1 1 1)
3、当条件为 s i z [ u ] > = s i z [ v ] siz[u]>=siz[v] siz[u]>=siz[v],则建边 w [ v , u ] = 0 w[v,u]=0 w[v,u]=0(表示 s i z [ u ] = = s i z [ v ] siz[u]==siz[v] siz[u]==siz[v],注意方向 v → u v\rightarrow u v→u,因为要保证最优性,就必须从小的向大的转移,尽可能的让大的和小的相等)
4、当条件为 s i z [ u ] > s i z [ v ] siz[u]>siz[v] siz[u]>siz[v],若 u = = v u==v u==v则直接输出 − 1 -1 −1(显然不成立),否则建边 w [ v , u ] = 1 w[v,u]=1 w[v,u]=1(表示 s i z [ u ] siz[u] siz[u]比 s i z [ v ] siz[v] siz[v]大 1 1 1)
5、当条件为 s i z [ u ] < = s i z [ v ] siz[u]<=siz[v] siz[u]<=siz[v],则建边 w [ u , v ] = 0 w[u,v]=0 w[u,v]=0(表示 s i z [ v ] = = s i z [ u ] siz[v]==siz[u] siz[v]==siz[u],注意方向 u → v u\rightarrow v u→v,因为要保证最优性,就必须从小的向大的转移,尽可能的让大的和小的相等)
接着,新建一个 0 0 0节点作为源点,向 i = 1 → n i=1\rightarrow n i=1→n所有点都连边 w [ 0 , i ] = 1 w[0,i]=1 w[0,i]=1(表示每个点至少有 1 1 1个糖果)
然后,我们跑一遍最长路(注意!其实求最长路时用 d f s dfs dfs模拟 s p f a spfa spfa过程,和直接跑 s p f a spfa spfa都能过(我都试写了一遍能 A A A),但有坑,后面会讲!),为什么是最长路呢?看看下面这张图(盗的):
图中的最短路表示 1 1 1点比 3 3 3点大 1 1 1,而另一边的约束条件会使得最后应该是最长路 1 1 1点比 3 3 3点大 2 2 2(很显然,因为肯定得满足约束最多的条件的情况才是合法的!)。
那么求出最长路后(记得判断有环时输出 − 1 -1 −1),因为要求的是糖果总和,于是累加一下 a n s + = d i s [ i ] , i ∈ [ 1 , n ] ans+=dis[i],\;i\in[1,n] ans+=dis[i],i∈[1,n],输出 a n s ans ans就好了。
(不看你会后悔:1、 a n s ans ans要开 l o n g l o n g long\;long longlong。2、醉醉重要的是一个很玄学的东西:后面 0 0 0向 1 → n 1\rightarrow n 1→n建边时一定要倒序,我也不知道为什么,顺序就是超时,倒序就能起飞。)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
struct node {
int v,w,next;
} d[N*2];
int tot,front[N],n,k;
long long ans;
int dis[N],vis[N],use[N];
queue <int> q;
void add(int u,int v,int w) {
d[++tot].v = v;
d[tot].w = w;
d[tot].next = front[u];
front[u] = tot;
}
bool spfa() {
while(!q.empty()) {
int u = q.front();
q.pop();
vis[u] = 0;
use[u] = 0;
for(int i=front[u]; i; i=d[i].next) {
int v = d[i].v, w = d[i].w;
if(dis[v] < dis[u] + w) {
dis[v] = dis[u] +w;
use[i]++;
if(use[i]>n-1) return false;
if(!vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
}
return true;
}
int main() {
scanf("%d %d",&n,&k);
for(int i=1; i<=k; i++) {
int op,u,v;
scanf("%d %d %d",&op,&u,&v);
if(op==1) {
add(u,v,0);
add(v,u,0);
}
if(op==2) add(u,v,1);
if(op==3) add(v,u,0);
if(op==4) add(v,u,1);
if(op==5) add(u,v,0);
if(op%2==0 && u==v) {
printf("-1\n");
return 0;
}
}
for(int i=1; i<=n; i++) {
vis[i] = 1;
dis[i] = 1;
use[i] = 1;
q.push(i);
}
if(!spfa()) {
printf("-1\n");
return 0;
}
for(int i=1; i<=n; i++)
ans+=dis[i];
printf("%lld\n",ans);
return 0;
}