总结:
1. 维护并查集的时候前面获得了pa=find(pa), 但是后面修改了p[pa],如果还使用之前的pa会造成错误,所以尽量在有改变之后都是用新的pa=find(a)
2. 并查集可以用来维护不同的种类(维护和根节点的距离),一般用d[pa] = d[b] - d[a] + d来维护,d具体而定
3. 区间加减变成s[r]和s[l-1]之间距离的维护
4. 并查集判断是否是树,判断否成环,判断边=点-1,有向图的话还需要判断出度和入读
5. 可以适当考虑下离线,先把所有查询读入,然后随着查询插入边或者倒着顺序查边,后者从前往后看就是删掉边了
真正的骗子
题目链接:https://www.acwing.com/activity/content/problem/content/6529/
1. 并查集维护相对关系
2. 01背包判断是否能唯一确定类别,当只有一种方案能组成是天使的时候,说明我们就可以确定是不是天使了
3. 在并查集树根元素位置进行dp
4. 背包具体方案的输出,需要使用二维数组,这样才能保存之前的信息
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1010;
int p[N], dist[N];
int f[N][N], st[N];
int w0[N], w1[N];
vector<int> res;
int n, p1, p2;
int find(int x){
if(x != p[x]){
int q = find(p[x]);
dist[x] += dist[p[x]];
p[x] = q;
}
return p[x];
}
int main(){
while(scanf("%d %d %d", &n, &p1, &p2), n || p1 || p2){
int m = p1 + p2;
for(int i = 1; i <= m; i ++) p[i] = i, dist[i] = 0;
for(int i = 1; i <= m; i ++ ) w0[i] = w1[i] = 0;
for(int i = 0; i <= m; i ++){
for(int j = 0; j <= m; j++){
f[i][j] = 0;
}
}
while(n -- ){
int a, b;
char jud[5];
scanf("%d %d %s", &a, &b, jud);
if(jud[0] == 'y'){
int pa = find(a), pb = find(b);
if(pa == pb) continue;
p[pa] = pb;
dist[pa] = (dist[b] - dist[a]) % 2;
}
else{
int pa = find(a), pb = find(b);
if(pa == pb) continue;
p[pa] = pb;
dist[pa] = (dist[b] - dist[a] + 1) % 2;
}
}
set<int> father;
for(int i = 1; i <= m; i++){
int pi = find(i);
if((dist[i]%2+2)%2) w1[pi] ++;
else w0[pi]++;
}
f[0][0] = 1;
for(int i = 1; i <= m; i++){
for(int j = 0; j <= m; j++){
if(i == find(i)){
if(j >= w0[i]) f[i][j] = f[i][j] + f[i-1][j-w0[i]];
if(j >= w1[i]) f[i][j] = f[i][j] + f[i-1][j-w1[i]];
}
else{
f[i][j] = f[i-1][j];
}
}
}
if(f[m][p1] == 1){
vector<int> res;
int value = p1;
for(int i = m; i >= 1; i--){
int flag = 0;
if(w0[i] != 0 && value >= w0[i]){
if(f[i-1][value - w0[i]] == 1){
value -= w0[i];
for(int j = 1; j <= m; j++ ){
if(find(j) == i && (dist[j]%2+2)%2 == 0) res.push_back(j);
}
flag = 1;
}
}
if(!flag && w1[i] != 0 && value >= w1[i]){
if(f[i-1][value - w1[i]] == 1){
value -= w1[i];
for(int j = 1; j <= m; j++){
if(find(j) == i && (dist[j]%2+2)%2 == 1) res.push_back(j);
}
}
}
}
sort(res.begin(), res.end());
for(auto c : res) printf("%d\n", c);
puts("end");
}
else puts("no");
}
return 0;
}
超市
题目链接:https://www.acwing.com/problem/content/147/
贪心:按照价值从大到小对商品进行排序,依次遍历,如果能卖出当前遍历到的物品,那么就卖出他,并且在尽可能靠后的一天卖出,
(假如你没有在尽可能靠后的一天卖出去的,可能会使得后面本来能卖出去的商品,卖不出去了)
什么才是尽可能靠后的一天呢,显然是deadline那天,但是那天可能被之前的价值更大的物品占据了
所以这里需要使用并查集来寻找尽可能靠后的一天,p[i]表示i这一天前面那一天没有被占据,
发现他竟然就是并查集,具体从代码不难看出
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e4+10;
int p[N];
struct Item{
int value, deadline;
bool operator < (const Item w) const{
return value > w.value;
}
}items[N];
int find(int x){
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
int main(){
int n;
while(scanf("%d", &n) == 1){
for(int i = 1; i <= n; i ++){
int a, b;
scanf("%d %d", &a, &b);
items[i] = {a, b};
}
sort(items + 1, items + 1 + n);
for(int i = 1; i <= 1e4; i ++) p[i] = i;
int res = 0;
for(int i = 1; i <= n; i ++){
int ddl = items[i].deadline, value = items[i].value;
int pi = find(ddl);
if(pi > 0){
p[pi] = pi - 1;
res += value;
}
}
printf("%d\n", res);
}
return 0;
}
石头剪子布
问题
1. 注意数据范围,发现可以暴力枚举哪个是裁判
2. 如果有多个符合条件,是不能确定,没有符合条件的是不可能,有唯一确定,并且排除其他人的可能性的最后的最后语句是确定的时间
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 510, M = 2010;
int p[N], d[N];
struct node{
int a, b, type;
}nodes[M];
int find(int x){
if(x != p[x]){
int q = find(p[x]);
d[x] += d[p[x]];
p[x] = q;
}
return p[x];
}
bool join(int a, int b, int type){
int pa = find(a), pb = find(b);
if(pa == pb){
if((d[b] - d[a] + type) % 3) return false;
}
else{
p[pa] = pb;
d[pa] = d[b] - d[a] + type;
}
return true;
}
int main(){
int n, m;
while(scanf("%d %d", &n, &m) == 2){
for(int i = 0; i < m; i ++){
int a, b; char op;
scanf("%d%c%d", &a, &op, &b);
if(op == '<') nodes[i] = {a, b, -1};
else if(op == '>') nodes[i] = {a, b, 1};
else nodes[i] = {a, b, 0};
}
int cnt = 0, who = -1, t = 0;
for(int i = 0; i < n; i ++){
int flag = 1;
for(int j = 0; j < n; j ++) p[j] = j, d[j] = 0;
for(int j = 0; j < m; j ++){
if(nodes[j].a == i || nodes[j].b == i) continue;
if(!join(nodes[j].a, nodes[j].b, nodes[j].type)) flag = 0, t = max(t, j);
if(!flag) break;
}
if(flag) cnt ++, who = i;
}
if(cnt > 1) puts("Can not determine");
else if(cnt == 0) puts("Impossible");
else printf("Player %d can be determined to be the judge after %d lines\n", who, t + (t != 0));
}
return 0;
}