周一
D. Weight the Tree(树形dp+输出方案)
这题妙啊
自己想的时候,发现好点是不相邻的,除非只有两个点的情况
那么求最多的好点显然可以树形dp,比较模板,但是我不知道如何保证总和最小。
在给点赋值上,给坏点赋1,好点赋为度数是最小的,因为对于好点,它最小为它的度数,它取到了最小值,同时坏点取1也取到了最小值。
要保证总和最小,就把它加入到dp状态里面即可,dp同时计算好点数量和总和。
输出方案的话,从根节点往下,根据dp的转移推出会选哪些点。如果当前是好点,那么儿子只能选坏点,如果当前是坏点,就看儿子的dp值哪一个大选哪个。不需要额外的数组记录方案
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 2e5 + 10;
struct node
{
int cnt, sum;
node operator + (const node& rhs) const
{
return {cnt + rhs.cnt, sum + rhs.sum};
}
bool operator > (const node& rhs) const
{
if(cnt != rhs.cnt) return cnt > rhs.cnt;
return sum < rhs.sum;
}
}dp[N][2];
vector<int> g[N];
int w[N], n;
void dfs(int u, int fa)
{
dp[u][0] = {0, 1};
dp[u][1] = {1, g[u].size()};
for(int v: g[u])
{
if(v == fa) continue;
dfs(v, u);
dp[u][1] = dp[u][1] + dp[v][0];
if(dp[v][0] > dp[v][1]) dp[u][0] = dp[u][0] + dp[v][0];
else dp[u][0] = dp[u][0] + dp[v][1];
}
}
void build(int u, int fa, int p)
{
w[u] = p ? g[u].size() : 1;
for(int v: g[u])
{
if(v == fa) continue;
if(p) build(v, u, 0);
else
{
if(dp[v][0] > dp[v][1]) build(v, u, 0);
else build(v, u, 1);
}
}
}
int main()
{
scanf("%d", &n);
_for(i, 1, n - 1)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
if(n == 2)
{
puts("2 2\n1 1");
return 0;
}
dfs(1, 0);
int choose = dp[1][1] > dp[1][0] ? 1 : 0;
printf("%d %d\n", dp[1][choose].cnt, dp[1][choose].sum);
build(1, 0, choose);
_for(i, 1, n) printf("%d ", w[i]); puts("");
return 0;
}
B. Linguistics(复杂贪心)
这题的贪心比较复杂。
首先是一些基本的条件判断,判断个数,再考虑其他
重点如何最优的去分配AB,BA
我们把字符串分割成一段段ABABAB
对于长度为奇数,发现可以匹配x个AB,y个BA,其中x+y = len / 2
对于长度为偶数,如果是A开头,显然优先分配AB,因为先分配BA有浪费字符
同理,B开头优先分配BA
但是还有一点,对于偶数段同种类型的,要先用长度小的再用长度大的。
考虑A开头的匹配BA,比如说BABA,只能匹配一个AB,然后浪费2个。BABABA能匹配2个AB,然后浪费两个
可以发现,一段会浪费2个字符,想要浪费最小,那么显然要段数最少。
怎么做到段数最少呢,显然是把长读小的段用了,剩下大段,这样留下了的段数会比较少,就浪费比较少。
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
void deal(int& c, int& d, int x)
{
if(c >= x) c -= x;
else
{
x -= c + 1;
c = 0;
d -= min(d, x);
}
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
string s; cin >> s;
int len = s.size();
s = " " + s;
int cnta = 0, cntb = 0, cntab = 0, cntba = 0;
_for(i, 1, len)
{
if(s[i] == 'A') cnta++;
if(s[i] == 'B') cntb++;
if(i < len && s[i] == 'A' && s[i + 1] == 'B') cntab++;
if(i < len && s[i] == 'B' && s[i + 1] == 'A') cntba++;
}
if(!(cnta == a + c + d && cntb == b + c + d && cntab >= c && cntba >= d))
{
puts("NO");
continue;
}
int i = 1, cnt = 0;
vector<int> va, vb;
while(i <= len)
{
int j = i;
while(j + 1 <= len && s[j + 1] != s[j]) j++;
int cur = j - i + 1;
if(cur > 1)
{
if(cur % 2 == 1) cnt += cur / 2;
else
{
if(s[i] == 'A') va.push_back(cur / 2);
else vb.push_back(cur / 2);
}
}
i = j + 1;
}
sort(va.begin(), va.end());
sort(vb.begin(), vb.end());
for(int x: va) deal(c, d, x);
for(int x: vb) deal(d, c, x);
puts(c + d <= cnt ? "YES" : "NO");
}
return 0;
}
D. New Year Concert(二分查找+线段树)
对于一个满足要求的区间,新加一个点,以此点为右端点,二分+线段树判断是否存在坏段。
二分查找的写法和二分答案不同,要区分开
#include<bits/stdc++.h>
#define l(k) (k << 1)
#define r(k) (k << 1 | 1)
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 2e5 + 10;
int t[N << 2], a[N], n;
int gcd(int a, int b) { return !b ? a: gcd(b, a % b); }
void up(int k)
{
t[k] = gcd(t[l(k)], t[r(k)]);
}
void modify(int k, int l, int r, int x, int p)
{
if(l == r)
{
t[k] = p;
return;
}
int m = l + r >> 1;
if(x <= m) modify(l(k), l, m, x, p);
else modify(r(k), m + 1, r, x, p);
up(k);
}
int ask(int k, int l, int r, int L, int R)
{
if(L <= l && r <= R) return t[k];
int m = l + r >> 1, res = 0;
if(L <= m) res = gcd(res, ask(l(k), l, m, L, R));
if(R > m) res = gcd(res, ask(r(k), m + 1, r, L, R));
return res;
}
int check(int l, int r)
{
int t1 = ask(1, 1, n, l, r);
int t2 = r - l + 1;
if(t1 > t2) return 1;
if(t1 == t2) return 0;
return -1;
}
int main()
{
scanf("%d", &n);
_for(i, 1, n)
{
scanf("%d", &a[i]);
modify(1, 1, n, i, a[i]);
}
int ans = 0;
_for(i, 1, n)
{
int l = 1, r = i, t = -1;
while(l <= r)
{
int m = l + r >> 1;
int cur = check(m, i);
if(cur == 1) r = m - 1;
else if(cur == -1) l = m + 1;
else
{
t = m;
break;
}
}
if(t != -1)
{
a[i] = 1e9 + 7;
modify(1, 1, n, i, a[i]);
ans++;
}
printf("%d ", ans);
}
puts("");
return 0;
}
周二
D. Shuffle(组合数学)
这题妙啊,我组合数学的题做的很少。
首先对于区间中有k个1,这个条件可以转化。如果整个区间1的个数大于等于k,那么其实每个区间内1个数小于等于k都可以操作,因为更多的部分可以不操作。当然,如果区间1个数小于k,那就一次都不能操作。
然后这道题的难点在于如何不算重,我们可以人为加一些限制使得不同操作中不会重复。
我们规定对当前区间操作时,两端的数字一定改变,这样就操作不同区间时就不会重复
具体来说,假设当前有cnt个1,我们要把这cnt个1分配到(l, r)中,如果端点为1,那么正好分配到区间内部,数字改变。如果端点为0,那么就分配1给端点,cnt-1。
那么此时的贡献就是C(len, cnt),也就是在区间内部len个位置分配cnt个1
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 5e3 + 10;
const int mod = 998244353;
int c[N][N], a[N], s[N], n, k;
int main()
{
_for(i, 0, 5e3) c[i][0] = 1;
_for(i, 1, 5e3)
_for(j, 1, i)
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
scanf("%d%d", &n, &k);
_for(i, 1, n)
{
scanf("%1d", &a[i]);
s[i] = s[i - 1] + a[i];
}
if(s[n] < k)
{
puts("1");
return 0;
}
int ans = 1;
_for(i, 1, n)
_for(j, i + 1, n)
{
int cnt = s[j] - s[i - 1];
if(cnt > k) continue;
cnt -= (a[i] == 0) + (a[j] == 0);
ans = (ans + c[j - i - 1][cnt]) % mod;
}
printf("%d\n", ans);
return 0;
}
B. Fibonacci Strings(贪心)
一开始想的是从小的开始贪心,然后样例都过不了
正解是从大的开始贪心。
首先可以由求和得出为当前为前i项的和
然后我们现在需要把字母分配到这i项上
方法是把当前最大的分配到最大的项上,注意要和上一次分配的字母不同
为什么呢,对于cmax >= f[i]
f[i] >= f[i - 1] + f[i - 3] ……
这个式子可以分i是奇数和偶数讨论
那么cmax如果不分配给f[i],要分配给后面的数,由可能会分配不完。
所以cmax要分配给f[i]
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int N = 110;
map<ll, int> mp;
int c[N], n;
ll f[N], sum;
int main()
{
sum = 2;
f[0] = f[1] = 1;
mp[1] = 0, mp[2] = 1;
_for(i, 2, 60)
{
f[i] = f[i - 1] + f[i - 2];
sum += f[i];
mp[sum] = i;
}
int T; scanf("%d", &T);
while(T--)
{
sum = 0;
scanf("%d", &n);
_for(i, 1, n) scanf("%d", &c[i]), sum += c[i];
if(!mp.count(sum))
{
puts("NO");
continue;
}
int last = -1, flag = 1;
for(int p = mp[sum]; p >= 0; p--)
{
int mx = 0, id;
_for(i, 1, n)
if(i != last && mx < c[i])
{
mx = c[i];
id = i;
}
if(mx < f[p])
{
flag = 0;
break;
}
c[id] -= f[p];
last = id;
}
puts(flag ? "YES" : "NO");
}
return 0;
}
周六
M. My University Is Better Than Yours(tarjan)
这题的关键在于发现缩点完后是一条链,这样就很好搞了
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 5e5 + 10;
vector<int> g[N], G[N];
int dfn[N], low[N], st[N], co[N], num[N], in[N], ans[N];
int top, n, m, cnt, id;
set<pair<int, int>> s;
void dfs(int u)
{
dfn[u] = low[u] = ++cnt;
st[++top] = u;
for(int v: g[u])
{
if(!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!co[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u])
{
id++;
while(1)
{
co[st[top--]] = id;
num[id]++;
if(st[top + 1] == u) break;
}
}
}
int main()
{
scanf("%d%d", &n, &m);
while(m--)
{
int pre = 0;
_for(i, 1, n)
{
int x; scanf("%d", &x);
if(pre) g[pre].push_back(x);
pre = x;
}
}
_for(i, 1, n)
if(!dfn[i])
dfs(i);
_for(u, 1, n)
for(int v: g[u])
if(co[u] != co[v] && !s.count({co[u], co[v]}))
{
s.insert({co[u], co[v]});
G[co[u]].push_back(co[v]);
in[co[v]]++;
}
queue<int> q;
_for(i, 1, id)
if(!in[i])
q.push(i);
vector<int> topo;
while(!q.empty())
{
int u = q.front(); q.pop();
topo.push_back(u);
for(int v: G[u])
if(--in[v] == 0)
q.push(v);
}
int sum = 0;
for(int i = topo.size() - 1; i >= 0; i--)
{
int cur = topo[i];
sum += num[cur];
ans[cur] = sum - 1;
}
_for(i, 1, n)
printf("%d ", ans[co[i]]);
puts("");
return 0;
}
周日
E - Multigate (二进制)
这题的关键在于发现最优的操作就是将最后k个 与变成或就是最优的
这样的话就维护前缀和后缀,预处理出来一个数组即可
还是要多练思维
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 2e5 + 10;
int a[N], b[N], k[N], n, q;
int ans[35][2][N];
int main()
{
scanf("%d%d", &n, &q);
_for(i, 1, n) scanf("%d", &a[i]);
_for(i, 1, n) scanf("%d", &b[i]);
for(int j = 30; j >= 0; j--)
_for(st, 0, 1)
{
k[0] = st;
_for(i, 1, n)
{
int cur = (a[i] >> j) & 1;
if(b[i] == 1) k[i] = k[i - 1] | cur;
else k[i] = k[i - 1] & cur;
}
int cur = 0, cost = 0;
for(int i = n; i >= 1; i--)
{
if(b[i] == 0)
{
ans[j][st][cost] = k[i] | cur;
cost++;
}
cur |= (a[i] >> j) & 1;
}
ans[j][st][cost] = st | cur;
}
int num = 0;
_for(i, 1, n)
if(b[i] == 0)
num++;
while(q--)
{
int x, k;
scanf("%d%d", &x, &k);
k = min(k, num);
int sum = 0;
_for(j, 0, 30)
{
int cur = (x >> j) & 1;
if(ans[j][cur][k]) sum += 1 << j;
}
printf("%d\n", sum);
}
return 0;
}
B - Potion(easy version)(思维)
这题的关键在于用分数表示,用分数表示可以发现每次增加的数是2的次幂,根据这点可以发现数的二进制特征
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) { return !b ? a : gcd(b, a % b); }
int main()
{
int T; scanf("%d", &T);
while(T--)
{
ll x, y, a, b;
scanf("%lld%lld%lld%lld", &x, &y, &a, &b);
ll d = gcd(x, y);
x /= d; y /= d;
if(x % 2 == 0 || y % 2 == 0)
{
puts("-1");
continue;
}
int mx = 0;
for(; (1LL << mx) <= x || (1LL << mx) <= y; mx++);
if((x ^ y) + 2 == 1LL << mx) printf("%d\n", mx + 1);
else puts("-1");
}
return 0;
}
P5960 【模板】差分约束算法
差分约束就是n个变量,m个不等式,然后求变量的值或变量的差的最值
像xi - xj <= ak
即xi <= xj + ak
这个式子非常像最短路里面的dv <= du + w
所以我们就从j到i连一条ak的边,跑最短路,就可以得到两个变量xi - xj <= 某个数,即可以求xi - xj的最大值
如果是>=那么就求最长度,可以求变量差的最小值
注意不等式的方向可以根据要求最大值或最小值来确定,方向不对的两边同乘-1即可
因为有负权边,用spfa求,可以用SLF优化
SLF优化非常像dijsktra的思想,即最短路短的先更新,对于SLF优化,要用双端队列,要加入队列的点v的最短路小于队首的点的最短路时,就把v加到队首,否则照常加到队尾。挺明显的。
如果跑最短路有负环或最长路有正环的就无解,判断的方法是一个点入队的次数超过n。
这题的目标不是求变量差值,而是求每个变量的值,那么我们加入一个超级源点,x0向每个点连一条边权为0的边,这样跑出来di就是xi可能的值
这样子相当于加入了xi - x0 <= 0
求出了xi - x0 <= t 即xi <= x0 + t,令x0=0,得xi <= t
所以是求出了xi<=0的最大的解。
还有一点是,将这些变量同时加减一个数也是解,这是显然的,因为所有的限制都是不等式,都是相对关系,绝对数量是不影响的。
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 5e3 + 10;
vector<pair<int, int>> g[N];
int d[N], cnt[N], vis[N], n, m;
bool spfa()
{
deque<int> q;
_for(i, 1, n) d[i] = 1e9;
q.push_back(0);
while(!q.empty())
{
int u = q.front(); q.pop_front();
vis[u] = 0;
for(auto [v, w]: g[u])
{
if(d[v] > d[u] + w)
{
d[v] = d[u] + w;
if(!vis[v])
{
if(++cnt[v] > n) return false;
if(!q.empty() && d[v] < d[q.front()]) q.push_front(v); //SLF优化
else q.push_back(v);
vis[v] = 1;
}
}
}
}
return true;
}
int main()
{
scanf("%d%d", &n, &m);
while(m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[v].push_back({u, w});
}
_for(i, 1, n) g[0].push_back({i, 0});
if(!spfa()) puts("NO");
else
{
_for(i, 1, n) printf("%d ", d[i]);
puts("");
}
return 0;
}
P4878 [USACO05DEC]Layout G(差分约束)
注意一些细节
隐含的xi+1 >= xi
对于负环,要用超级源点来判断,从1开始判断不一定联通,也就是说要跑两次spfa
跑两次spfa注意初始化
其他基本是模板了
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e3 + 10;
vector<pair<int, int>> g[N];
int d[N], cnt[N], vis[N], n, m1, m2;
bool spfa(int u)
{
deque<int> q;
_for(i, 1, n) d[i] = 1e9, vis[i] = 0, cnt[i] = 0;
d[u] = 0;
q.push_back(u);
vis[u] = 1;
while(!q.empty())
{
int u = q.front(); q.pop_front();
vis[u] = 0;
for(auto [v, w]: g[u])
if(d[v] > d[u] + w)
{
d[v] = d[u] + w;
if(!vis[v])
{
if(++cnt[v] > n) return false;
if(!q.empty() && d[v] < d[q.front()]) q.push_front(v);
else q.push_back(v);
vis[v] = 1;
}
}
}
return true;
}
int main()
{
scanf("%d%d%d", &n, &m1, &m2);
while(m1--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[u].push_back({v, w});
}
while(m2--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[v].push_back({u, -w});
}
_for(i, 1, n - 1) g[i + 1].push_back({i, 0});
_for(i, 1, n) g[0].push_back({i, 0});
if(!spfa(0)) puts("-1");
else
{
spfa(1);
printf("%d\n", d[n] == 1e9 ? -2 : d[n]);
}
return 0;
}
Integer Intervals(差分约束)
用前缀和表示即可
因为会有负下标,所以都加1
从0开始跑,求出的是smx - s0 >= t
smx - s0正好就是有多少个数
t即是答案
最大值我开始取1e4+1,然后T了
应该输入的时候取最大值
#include<cstdio>
#include<queue>
#include<vector>
#include<utility>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e4 + 10;
vector<pair<int, int>> g[N];
int d[N], vis[N], m, mx;
void spfa(int u)
{
queue<int> q;
_for(i, 1, mx) d[i] = -1e9;
d[u] = 0;
q.push(u);
vis[u] = 1;
while(!q.empty())
{
int u = q.front(); q.pop();
vis[u] = 0;
rep(i, 0, g[u].size())
{
int v = g[u][i].first, w = g[u][i].second;
if(d[v] < d[u] + w)
{
d[v] = d[u] + w;
if(!vis[v])
{
q.push(v);
vis[v] = 1;
}
}
}
}
}
int main()
{
scanf("%d", &m);
while(m--)
{
int a, b;
scanf("%d%d", &a, &b);
a++; b++;
mx = max(mx, b);
g[a - 1].push_back(make_pair(b, 2));
}
_for(i, 1, 1e4 + 1)
{
g[i - 1].push_back(make_pair(i, 0));
g[i].push_back(make_pair(i - 1, -1));
}
spfa(0);
printf("%d\n", d[mx]);
return 0;
}
P3275 [SCOI2011]糖果(差分约束/缩点+dp)
这题可以用差分约束做,求的是每个变量的值,不是差值,所以建立一个超级源点。因为题目说至少一个糖果,所以超级源点向每个源点连长度为1的边,这样求出的就是每个变量大于等于1的最小解,符合题目要求。
注意统计答案时开long long,我写时感觉不会爆int,结果还是爆了。不是很确定不会爆int的时候要开long long
然后要用SLF优化。
但是最后有两个hack数据还是T了,应该是spfa还是被卡了
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
vector<pair<int, int>> g[N];
int d[N], vis[N], cnt[N], n, m;
bool spfa(int u)
{
deque<int> q;
_for(i, 1, n) d[i] = -1e9;
d[u] = 0;
q.push_back(u);
vis[u] = 1;
while(!q.empty())
{
int u = q.front(); q.pop_front();
vis[u] = 0;
for(auto [v, w]: g[u])
if(d[v] < d[u] + w)
{
d[v] = d[u] + w;
if(!vis[v])
{
if(++cnt[v] > n + 1) return false;
if(!q.empty() && d[v] > d[q.front()]) q.push_front(v); //用SLF优化保险
else q.push_back(v);
vis[v] = 1;
}
}
}
return true;
}
int main()
{
scanf("%d%d", &n, &m);
while(m--)
{
int op, a, b;
scanf("%d%d%d", &op, &a, &b);
if(op == 1)
{
g[a].push_back({b, 0});
g[b].push_back({a, 0});
}
else if(op == 2)
g[a].push_back({b, 1});
else if(op == 3)
g[b].push_back({a, 0});
else if(op == 4)
g[b].push_back({a, 1});
else
g[a].push_back({b, 0});
}
_for(i, 1, n) g[0].push_back({i, 1});
if(!spfa(0)) puts("-1");
else
{
ll ans = 0; //不要忘了开long long 不确定的时候还是开为好
_for(i, 1, n) ans += d[i];
printf("%lld\n", ans);
}
return 0;
}
缩点的来源在于有边权为0的双向边,这样就相当于一个点,所以可以先把边权为0的边加入,然后进行缩点。然后进行拓扑排序,如果有环就是无解。然后要求最长路,注意最长路不是最短路,因为每条边都要满足
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
vector<pair<int, int>> g[N], G[N];
int x[N], a[N], b[N], in[N], dp[N], n, m;
int dfn[N], low[N], st[N], co[N], num[N], top, cnt, id;
set<pair<int, int>> s;
void dfs(int u)
{
dfn[u] = low[u] = ++cnt;
st[++top] = u;
for(auto [v, w]: g[u])
{
if(!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!co[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u])
{
id++;
while(1)
{
co[st[top--]] = id;
num[id]++;
if(st[top + 1] == u) break;
}
}
}
int main()
{
scanf("%d%d", &n, &m);
_for(i, 1, m) scanf("%d%d%d", &x[i], &a[i], &b[i]);
_for(i, 1, m)
{
if(x[i] == 1)
{
g[a[i]].push_back({b[i], 0});
g[b[i]].push_back({a[i], 0});
}
else if(x[i] == 3)
g[b[i]].push_back({a[i], 0});
else if(x[i] == 5)
g[a[i]].push_back({b[i], 0});
}
_for(i, 1, n)
if(!dfn[i])
dfs(i);
_for(u, 1, n)
for(auto [v, w]: g[u])
if(co[u] != co[v] && !s.count({co[u], co[v]}))
{
G[co[u]].push_back({co[v], w});
s.insert({co[u], co[v]});
in[co[v]]++;
}
_for(i, 1, m)
{
int u = co[a[i]], v = co[b[i]];
if(x[i] == 2)
{
G[u].push_back({v, 1});
in[v]++;
}
else if(x[i] == 4)
{
G[v].push_back({u, 1});
in[u]++;
}
}
cnt = 0;
queue<int> q;
vector<int> topo;
_for(i, 1, id) dp[i] = -1e9;
_for(i, 1, id)
if(!in[i])
{
q.push(i);
dp[i] = 1;
}
while(!q.empty())
{
int u = q.front(); q.pop();
cnt++;
for(auto [v, w]: G[u])
{
dp[v] = max(dp[v], dp[u] + w);
if(--in[v] == 0) q.push(v);
}
}
if(cnt != id) puts("-1");
else
{
ll ans = 0;
_for(i, 1, id)
ans += 1LL * num[i] * dp[i];
printf("%lld\n", ans);
}
return 0;
}
P2294 [HNOI2005]狡猾的商人(差分约束)
比较裸的差分约束
要判断负环的话要建一个超级源点保证联通
等式转化成两个不等式即可
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 110;
vector<pair<int, int>> g[N];
int d[N], cnt[N], vis[N], n, m;
bool spfa()
{
deque<int> q;
_for(i, 1, n + 1) cnt[i] = 0, d[i] = 1e9, vis[i] = 0;
q.push_back(0);
vis[0] = 1;
while(!q.empty())
{
int u = q.front(); q.pop_front();
vis[u] = 0;
for(auto [v, w]: g[u])
if(d[v] > d[u] + w)
{
d[v] = d[u] + w;
if(!vis[v])
{
if(++cnt[v] > n + 1) return false;
if(!q.empty() && d[v] < d[q.front()]) q.push_front(v);
else q.push_back(v);
vis[v] = 1;
}
}
}
return true;
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
_for(i, 0, n + 1) g[i].clear();
while(m--)
{
int l, r, w;
scanf("%d%d%d", &l, &r, &w);
l++; r++;
g[l - 1].push_back({r, w});
g[r].push_back({l - 1, -w});
}
_for(i, 1, n + 1) g[0].push_back({1, 0});
puts(spfa() ? "true" : "false");
}
return 0;
}