算是一个模板题吧
给出[l,r]的区间和,相当于s[r]-s[l]
一旦已经知道了 s[a]-s[b],s[b]-s[c],显然再给出一条[a,c]就可以判断"账本的真假"了
将每条这样的信息(l,r,w),l,r放入一个集合中,
用并查集来维护,并维护cha[l]=s[root]-s[l],cha[r]=s[root]-s[r]
若 l,r已经在同一个集合中,就直接查询cha[l]-cha[r],判读与w是否相等
合并操作是灵魂
实话说如果没有对并查集合并操作和路径压缩理解够深刻 是不能写出来的
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=505;
int T;
int fa[maxn],val[maxn];
int find(int x){
if(fa[x]==x)return x;
else {
int oldfa=fa[x];
fa[x]=find(fa[x]);
val[x]+=val[oldfa];
return fa[x];
}
}
void solve();
int main(){
cin>>T;
while(T--)solve();
return 0;
}
void solve(){
int n,m;
cin>>n>>m;
for(int i=0;i<=n;i++)
fa[i]=i,val[i]=0;
bool cmp=1;
while(m--){
int x,y,z;
cin>>x>>y>>z;
x--;
int fx=find(x),fy=find(y);
if(fx!=fy){
fa[fy]=fx;
val[fy]=val[x]-val[y]-z;
}
else if(val[x]-val[y]!=z)cmp=0;
}
if(cmp)cout<<"true"<<endl;
else cout<<"false"<<endl;
}
每次给出两个昆虫的关系(异性关系),然后发现这些条件中是否有悖论
和上一个题目差不多 维护x->rootx 的距离就好
#include <cstdio>
const int maxn = 2000 + 10;
int pre[maxn];
int r[maxn]; // 与根节点的关系,如果值为1则为异性如果为0则为同性
int find(int x) {
int t = pre[x];
if (pre[x] != x) {
pre[x] = find(pre[x]);
r[x] = (r[x] + r[t]) % 2; // 更新关系
}
return pre[x];
}
int main() {
// freopen("input.txt", "r", stdin);
int flag;
int kase = 0;
int T;
int x, y;
int n, m;
scanf("%d", &T);
while(T--) {
flag = 1;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
pre[i] = i;
r[i] = 0;
}
while(m--) {
scanf("%d%d", &x, &y);
if (!flag) continue;
int rx = find(x);
int ry = find(y);
if (rx == ry) {
if ((r[x] - r[y]+2) % 2 == 0) {
flag = 0;
}
}
else {
pre[ry] = rx;
r[ry] = (r[x] - r[y] + 1) % 2;
}
}
printf("Scenario #%d:\n",++kase);
if(flag)
printf("No suspicious bugs found!\n\n");
else
printf("Suspicious bugs found!\n\n");
}
return 0;
}
这个题是上面的题型的升级版 首先找到规律
0吃1
1吃2
2吃0(2吃3)
发现后面减去前面始终为1 所以就是 被吃 减 吃==1
剩下的就差不太多了
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=5e4+5;
int n,m,ans;
int fa[maxn],val[maxn];
int find(int x){
if(x==fa[x])return x;
int oldfa=fa[x];
fa[x]=find(fa[x]);
val[x]=(val[x]+val[oldfa])%3;
return fa[x];
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)fa[i]=i,val[i]=0;
for(int i=1;i<=m;i++){
int c,x,y;
cin>>c>>x>>y;
if(x>n||y>n||(c==2&&x==y)){
ans++;continue;
}
int fx=find(x),fy=find(y);
if(c==1){
if(fx!=fy){
fa[fy]=fx;
val[fy]=(val[x]-val[y]+3)%3;
}else if((val[y]-val[x]+3)%3)
ans++;
}else{
if(fx==fy){
if(((val[y]-val[x]+3)%3)!=1)
ans++;
}
else{
fa[fy]=fx;
val[fy]=(val[x]-val[y]+1+3)%3;
}
}
}
cout<<ans<<endl;
return 0;
}
因为朋友之间是双向边 只要在合并的时候维护一下集合的最小值就好 最后每个集合只要保证有一个最小的人去贿赂就好
思考:如果是单向边的话 那就要tarjan缩点 然后对每个缩完点入度为0的点中找出最小的一个 累加