luogu 2294 [HNOI2005]狡猾的商人
Description
***姹接到一个任务,为税务部门调查一位商人的账本,看看账本是不是伪造的。账本上记录了n个月以来的收入情况,其中第i 个月的收入额为Ai(i=1,2,3...n-1,n), 。当 Ai大于0时表示这个月盈利Ai 元,当 Ai小于0时表示这个月亏损Ai 元。所谓一段时间内的总收入,就是这段时间内每个月的收入额的总和。 ***姹的任务是秘密进行的,为了调查商人的账本,她只好跑到商人那里打工。她趁商人不在时去偷看账本,可是她无法将账本偷出来,每次偷看账本时她都只能看某段时间内账本上记录的收入情况,并且她只能记住这段时间内的总收入。 现在,***姹总共偷看了m次账本,当然也就记住了m段时间内的总收入,你的任务是根据记住的这些信息来判断账本是不是假的。
Input
第一行为一个正整数w,其中w < 100,表示有w组数据,即w个账本,需要你判断。每组数据的第一行为两个正整数n和m,其中n < 100,m < 1000,分别表示对应的账本记录了多少个月的收入情况以及偷看了多少次账本。接下来的m行表示***姹偷看m次账本后记住的m条信息,每条信息占一行,有三个整数s,t和v,表示从第s个月到第t个月(包含第t个月)的总收入为v,这里假设s总是小于等于t。
Output
包含w行,每行是true或false,其中第i行为true当且仅当第i组数据,即第i个账本不是假的;第i行为false当且仅当第i组数据,即第i个账本是假的。
Sample Input
3 3
1 2 10
1 3 -5
3 3 -15
5 3
1 5 100
3 5 50
1 2 51
Sample Output
false
分析
解法一 差分约束
s月到t月共收入v元,从s-1到t连一条长度为v的边
图没有环
对于每一个入度为零的点,跑两遍SPFA(最长/最短)
比较各个点的(能到达的点)两个距离是否一样,不一样就false,
一样就继续找入度为零的点
注意每一次SPFA前要初始化
因为这个距离是相对起点而言,所以各个起点是独立的
不能只在最后判断最长最短是否一样
1 /********************* 2 User:Mandy.H.Y 3 Language:c++ 4 Problem:luogu2294 5 Algorithm: 6 *********************/ 7 #include<bits/stdc++.h> 8 9 using namespace std; 10 11 const int maxn = 105; 12 const int maxm = 1005; 13 const int mod = 1e5; 14 15 int W,n,m,size; 16 int first[maxn]; 17 int dis1[maxn],dis2[maxn],ind[maxn]; 18 int q[100005],l,r; 19 bool vis[maxn]; 20 21 struct Edge{ 22 int v,nt,w; 23 }edge[maxm]; 24 25 template<class T>inline void read(T &x){ 26 x = 0;bool flag = 0;char ch = getchar(); 27 while(!isdigit(ch)) flag |= ch == '-',ch = getchar(); 28 while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); 29 if(flag) x = -x; 30 } 31 32 void file(){ 33 freopen("2294.in","r",stdin); 34 freopen("2294.out","w",stdout); 35 } 36 37 void init(){ 38 size = 0; 39 memset(first,0,sizeof(first)); 40 memset(edge,0,sizeof(edge)); 41 memset(ind,0,sizeof(ind)); 42 } 43 44 void eadd(int u,int v,int w){ 45 edge[++size].v = v; 46 edge[size].w = w; 47 edge[size].nt = first[u]; 48 first[u] = size; 49 } 50 51 void readdata(){ 52 read(n);read(m); 53 for(int i = 1;i <= m; ++ i){ 54 int u,v,w; 55 read(u);read(v);read(w); 56 eadd(u-1,v,w); 57 ind[v] ++; 58 } 59 } 60 61 bool SPFA(int x){ 62 l = r = 0; 63 q[r++] = x; 64 memset(vis,0,sizeof(vis)); 65 while(l != r){ 66 int u = q[l]; 67 l ++ ; l %= mod; 68 int rr = (r - 1 + mod) % mod; 69 if(l != r) if(dis1[q[l]] > dis1[q[rr]]) swap(q[l],q[rr]); 70 vis[u] = 0; 71 for(int i = first[u];i;i = edge[i].nt){ 72 int v = edge[i].v; 73 int w = edge[i].w; 74 if(dis1[v] > dis1[u] + w){ 75 dis1[v] = dis1[u] + w; 76 if(!vis[v]){ 77 q[r++] = v; 78 vis[v] = 1; 79 r %= mod; 80 int rr = (r - 1 + mod) % mod; 81 if(l != r) if(dis1[q[l]] > dis1[q[rr]]) swap(q[l],q[rr]); 82 } 83 } 84 } 85 } 86 87 88 l = r = 0; 89 q[r++] = x; 90 memset(vis,0,sizeof(vis)); 91 while(l != r){ 92 int u = q[l]; 93 l ++ ; l %= mod; 94 int rr = (r - 1 + mod) % mod; 95 if(l != r) if(dis2[q[l]] < dis2[q[rr]]) swap(q[l],q[rr]); 96 vis[u] = 0; 97 for(int i = first[u];i;i = edge[i].nt){ 98 int v = edge[i].v; 99 int w = edge[i].w; 100 if(dis2[v] < dis2[u] + w){ 101 dis2[v] = dis2[u] + w; 102 if(!vis[v]){ 103 q[r++] = v; 104 vis[v] = 1; 105 r %= mod; 106 int rr = (r - 1 + mod) % mod; 107 if(l != r) if(dis2[q[l]] < dis2[q[rr]]) swap(q[l],q[rr]); 108 } 109 } 110 } 111 } 112 113 for(int i = 1;i <= n; ++ i){ 114 if(dis1[i] != 0x3f3f3f3f && dis1[i] != dis2[i]){ 115 return 0; 116 } 117 } 118 return 1; 119 120 } 121 122 void work(){ 123 init(); 124 readdata(); 125 126 for(int i = 0;i <= n; ++ i){ 127 if(ind[i] == 0){ 128 129 memset(dis1,0x3f3f3f3f,sizeof(dis1)); 130 memset(dis2,128,sizeof(dis2)); 131 132 dis1[i] = 0; 133 dis2[i] = 0; 134 if(!SPFA(i)) { 135 puts("false"); 136 return; 137 } 138 } 139 } 140 puts("true"); 141 } 142 143 int main(){ 144 // file(); 145 read(W); 146 while(W -- ) work(); 147 return 0; 148 }
解法二 带权并查集