目录
P3605 [USACO17JAN] Promotion Counting P
并查集
P1111 修复公路
题意:
思路:
建最小生成树的过程中记录边的最大值即可
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e3+10;
const int INF = 0x3f3f3f3f;
struct node{
int x,y,t;
};
int fa[N];
int get_fa(int x)
{
if(fa[x] == x)
return x;
return fa[x] = get_fa(fa[x]);
}
bool cmp(node a,node b)
{
return a.t<b.t;
}
vector<node>g;
void init(int n){
for(int i = 1;i<=n;i++)
fa[i] = i;
}
signed main(){
int n,m;
cin>>n>>m;
for(int i =0;i<m;i++)
{
int x,y,t;
cin>>x>>y>>t;
g.push_back({x,y,t});
}
init(n);
sort(g.begin(),g.end(),cmp);
int ans = -INF,cnt = 0;;
for(int i = 0;i<g.size();i++)
{
int x = g[i].x,y = g[i].y,t = g[i].t;
int newx = get_fa(x),newy = get_fa(y);
if(newx!=newy)
{
fa[newx] = newy;
ans = max(ans,t);
cnt++;
}
}
if(cnt < n-1)
cout<<-1<<endl;
else{
cout<<ans<<endl;
}
}
P1525 [NOIP 2010 提高组] 关押罪犯
题意:
思路:
题目中说了在两座监狱分配,使用扩展并查集。
首先对怨气值进行降序排序,顺序处理每条边,令(u,v+n),(v,u+n)进行合并,并对(u,u+n),(v,v+n)进行检查
如果存在这种矛盾边,那么答案就是现在正在处理的边权
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e4+10;
const int INF = 0x3f3f3f3f;
int n,m;
int fa[2*N];
struct node{
int x,y,t;
};
vector<node>g;
bool cmp(node a,node b)
{
return a.t>b.t;
}
void init(int n)
{
for(int i = 1;i<=2*n;i++)
fa[i] = i;
}
int get_fa(int x)
{
if(fa[x] == x)
return x;
return fa[x] = get_fa(fa[x]);
}
void vnio(int x,int y)
{
int newx = get_fa(x),newy = get_fa(y);
fa[newx] = newy;
}
void solve(){
cin>>n>>m;
for(int i = 0;i<m;i++)
{
int x,y,t;
cin>>x>>y>>t;
g.push_back({x,y,t});
}
init(n);
sort(g.begin(),g.end(),cmp);
for(auto it:g)
{
vnio(it.x,it.y+n);
vnio(it.y,it.x+n);
if(get_fa(it.x) == get_fa(it.x+n)|get_fa(it.y) == get_fa(it.y+n))
{
cout<<it.t<<endl;
return ;
}
}
cout<<0<<endl;
}
signed main(){
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
P4185 [USACO18JAN] MooTube G
题意:
思路:
依旧是离线处理+并查集,需要对边权,K值从大到小排序。这么做的目的是因为相关性是最小的边,所以要从大的处理,这样只需考虑往并查集加边就行。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+10;
const int INF = 0x3f3f3f3f;
int n,m;
int fa[N],siz[N];
struct node1{
int x,y,t;
};
vector<node1>g;
struct node2{
int id,k,u;
};
vector<node2>Q;
bool cmp1(node1 a,node1 b)
{
return a.t>b.t;
}
bool cmp2(node2 a,node2 b)
{
return a.k>b.k;
}
void init(int n)
{
for(int i = 1;i<=n;i++)
fa[i] = i,siz[i] = 1;
}
int get_fa(int x)
{
if(fa[x] == x)
return x;
return fa[x] = get_fa(fa[x]);
}
void vnio(int x,int y)
{
int newx = get_fa(x),newy = get_fa(y);
if(newx == newy)
return;
fa[newx] = newy;
siz[newy]+=siz[newx];
}
void solve(){
cin>>n>>m;
vector<int>ans(m);
for(int i = 0;i<n-1;i++)
{
int x,y,t;
cin>>x>>y>>t;
g.push_back({x,y,t});
}
for(int i =0;i<m;i++)
{
int k,u;
cin>>k>>u;
Q.push_back({i,k,u});
}
init(n);
sort(g.begin(),g.end(),cmp1);
sort(Q.begin(),Q.end(),cmp2);
int cur = 0;
for(int i = 0;i<m;i++)
{
while(cur<n-1&&Q[i].k<=g[cur].t)
{
vnio(g[cur].x,g[cur].y);
cur++;
}
ans[Q[i].id] = siz[get_fa(Q[i].u)]-1;
}
for(int i = 0;i<m;i++)
cout<<ans[i]<<endl;
}
signed main(){
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
P2024 [NOI2001] 食物链
题意:
思路:
扩展域并查集,分为同类,捕食,天敌三个域
矛盾的评估:当op == 1时,(u+n,v),(v+n,u),意义就是同类不能吃同类
op == 2时,(u,v),(v+n,u),意义就是捕食关系不可能时同类,吃的关系不能反过来。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 11380;
const int INF = 1e18;
const int N = 3 * 1e5 + 10;
int T = 1;
struct node {
int u;
int v;
int e;
};
int fa[N];
int get(int x)
{
if (x == fa[x])
return x;
return fa[x] = get(fa[x]);
}
void merge(int x, int y)
{
fa[x] = y;
}
void solve() {
int n, k;
cin >> n >> k;
for (int i = 1; i <= N; i++)
fa[i] = i;
int ans = 0;
for (int i = 0; i < k; i++) {
int op, u, v;
cin >> op >> u >> v;
int u1 = u + n, v1 = v + n;
if (u > n || v > n)
{
ans++;
continue;
}
if (op == 1)
{
if (get(u1) == get(v) || get(v1) == get(u))
{
ans++;
continue;
}
fa[get(u)] = get(v), fa[get(u1)] = get(v1),fa[get(u1+n)] = get(v1+n);
}
else {
if (get(u) == get(v) || get(v1) == get(u))
{
ans++;
continue;
}
fa[get(u1)] = get(v);
fa[get(u1+n)] = get(v1);
fa[get(u)] = get(v1+n);
}
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
// cin >> T;
while (T--) {
solve();
}
}
P1197 [JSOI2008] 星球大战
题意:
思路:
直接逆向思考,从全部摧毁到逐步恢复,细节请看代码。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e5+10;
const int INF = 0x3f3f3f3f;
int n,m;
int fa[N],siz[N];
struct node1{
int x,y;
};
vector<node1>g;
vector<int>G[N];
map<int,int>ck,hd;
int res = 0;
void init(int n)
{
for(int i = 0;i<=n;i++)
fa[i] = i,siz[i] = 1;
}
int get_fa(int x)
{
if(fa[x] == x)
return x;
return fa[x] = get_fa(fa[x]);
}
void vnio(int x,int y)
{
int newx = get_fa(x),newy = get_fa(y);
if(newx == newy)
return;
res--;
fa[newx] = newy;
}
void solve(){
cin>>n>>m;
init(n);
for(int i = 0;i<m;i++)
{
int x,y;
cin>>x>>y;
g.push_back({x,y});
G[x].push_back(y);
G[y].push_back(x);
}
int k ;
cin>>k;
vector<int>Q(k),ans(k);
for(int i = 0;i<k;i++)
cin>>Q[i],ck[Q[i]] = 1;
for(int i = 0;i<g.size();i++)
{
int x = g[i].x,y = g[i].y;
// cout<<x<<' '<<y<<endl;
if(ck.count(x)||ck.count(y))
continue;
//cout<<x<<' '<<y<<endl;
vnio(x,y);
}
res = 0;
for(int i = 0;i<n;i++)
if(fa[i] == i&&!ck.count(i)){
res++;
}
for(int i = k-1;i>=0;i--)
{
ans[i] = res;
res++;
for(int j = 0;j<G[Q[i]].size();j++)
{
if(ck.count(G[Q[i]][j]))
continue;
vnio(Q[i],G[Q[i]][j]);
}
ck.erase(Q[i]);
}
cout<<res<<endl;
for(int i = 0;i<k;i++)
cout<<ans[i]<<endl;
}
signed main(){
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
P1196 [NOI2002] 银河英雄传说
题意:
思路:
本题在并查集的基础上还要正确维护一个信息,设d[x]表示x战舰前面有的数量。
维护方式:
- 当合并时(假设令x是y的孩子),d[x] = siz[v](打延迟标记)。
- 查询的时候,d[x]+=d[fa[x]]
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e5+10;
const int INF = 0x3f3f3f3f;
int n,m;
int fa[N],siz[N],d[N];
int res = 0;
void init(int n)
{
for(int i = 0;i<=n;i++)
fa[i] = i,siz[i] = 1;
}
int get_fa(int x)
{
if(fa[x] == x)
return x;
int q = get_fa(fa[x]);
d[x]+=d[fa[x]];
return fa[x] = q;
}
void vnio(int x,int y)
{
int newx = get_fa(x),newy = get_fa(y);
if(newx == newy)
return;
d[newx] = siz[newy];
siz[newy]+=siz[newx];
fa[newx] = newy;
}
void solve(){
int t ;
cin>>t;
init(30010);
while(t--)
{
char op;
cin>>op;
if(op == 'M')
{
int i,j;
cin>>i>>j;
vnio(i,j);
}
else{
int i,j;
cin>>i>>j;
if(get_fa(i)!=get_fa(j))
cout<<-1<<endl;
else{
cout<<abs(d[i]-d[j])-1<<endl;
}
}
}
}
signed main(){
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
P1955 [NOI2015] 程序自动分析
题意:
思路:
并查集,先处理相等的情况,然后再处理不等的情况找矛盾。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 11380;
const int INF = 1e18;
const int N = 3 * 1e5 + 10;
int T = 1;
bool cmp(int a, int b)
{
return a > b;
}
struct node {
int u, v;
int e;
};
int fa[N];
int find(int x)
{
int a = x;
while (x != fa[x])
x = fa[x];
while (a != fa[a])
{
int z = fa[a];
fa[a] = x;
a = z;
}
return x;
}
void solve() {
int n;
cin >> n;
for (int i = 1; i <= 2 * n; i++)
fa[i] = i;
vector<node>a(n+1);
vector<int>b;
int co = 0;
for (int i = 1; i <= n; i++)
{
int u, v, e;
cin >> u >> v >> e;
a[i].u = u, a[i].v = v, a[i].e = e;
b.push_back(u), b.push_back(v);
}
sort(b.begin(), b.end());
b.erase(unique(b.begin(), b.end()), b.end());
for (int i = 1; i <= n; i++)
{
int pos1 = lower_bound(b.begin(), b.end(), a[i].u) - b.begin() + 1;
int pos2 = lower_bound(b.begin(), b.end(), a[i].v) - b.begin() + 1;
a[i].u = pos1, a[i].v = pos2;
}
int fl = 0;
for (int i = 1; i <= n; i++) {
int fx = find(a[i].u);
int fy = find(a[i].v);
if (a[i].e == 1)
{
fa[fx] = fy;
}
}
for (int i = 1; i <= n; i++) {
if (!a[i].e)
{
int fx = find(a[i].u);
int fy = find(a[i].v);
if (fx == fy)
{
fl = 1;
break;
}
}
}
cout << (fl ? "NO":"YES") << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while (T--) {
solve();
}
}
ST表
P3865 【模板】ST 表 && RMQ 问题
题意:
思路:
使用ST表模版即可,注意使用'\n'换行符,不然会超时。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+10;
const int INF = 0x3f3f3f3f;
int n,m,up;
int st[N][64];
int Logn[N];
vector<int>arr(N);
void init(int n)
{
Logn[1] = 0;
Logn[2] =1;
for(int i = 3;i<N;i++)
{
Logn[i] = Logn[i/2]+1;
}
for(int j= 1;j<=20;j++)
{
for(int i = 1;i+(1<<j)-1<=n;i++)
{
st[i][j] = max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
void solve(){
cin>>n>>m;
//up = (int)(log(n)/log(2))+1;
for(int i = 1;i<=n;i++)
cin>>st[i][0];
init(n);
while(m--)
{
int l,r;
cin>>l>>r;
int t1 = Logn[r-l+1];
cout<<max(st[l][t1],st[r-(1<<t1)+1][t1])<<'\n';
}
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
P1198 [JSOI2008] 最大数
题意:
思路:
每次都加到序列的末尾,那么每次加入时只需要处理新加进来的即可,因为前面已经处理的部分不会被影响、本题要把ST表的定义进行修改,ST[i][j] 代表的最大值。相当于倒序处理、
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10;
const int INF = 0x3f3f3f3f;
int n,m,up = 21;
int st[N][64];
void change(int n){
for(int i = 1;n-(1<<i)+1>=1;i++)
st[n][i] = max(st[n][i-1],st[n-(1<<(i-1))][i-1]);
}
void solve(){
int t,d;
cin>>t>>d;
int old = 0;
while(t--){
char op ;
cin>>op;
if(op == 'A')
{
int tmp ;
cin>>tmp;
tmp = (tmp+old)%d;
st[++n][0] = tmp;
change(n);
}
else{
int len ;
cin>>len;
int l = n-len+1,r = n;
int s = log(r-l+1)/log(2);
cout<<(old = max(st[r][s],st[l+(1<<s)-1][s]))<<endl;
}
}
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
树状数组
P3374 【模板】树状数组 1
题意:

思路:
单点修改,区间查询使用树状数组模版即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10;
const int INF = 0x3f3f3f3f;
int tr1[N];
int n;
int lowbit(int x)
{
return x&-x;
}
void add(int x,int c)
{
while(x<=n)
{
tr1[x]+=c;
x+=lowbit(x);
}
}
int get_sum(int r)
{
int res = 0;
while(r>0)
{
res+=tr1[r];
r-=lowbit(r);
}
return res;
}
void solve(){
int m ;
cin>>n>>m;
for(int i = 0;i<n;i++)
{
int c ;
cin>>c;
add(i+1,c);
}
while(m--)
{
int op,x,y;
cin>>op>>x>>y;
if(op == 1)
add(x,y);
else
cout<<get_sum(y)-get_sum(x-1)<<endl;
}
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
P3368 【模板】树状数组 2
题意:

思路:
用两个树状数组,维护两个差分数组、
假设d[i]是差分数组,考虑求(1,r)的和
可分为
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5+10;
const int INF = 0x3f3f3f3f;
int tr1[N],tr2[N];
int n;
int lowbit(int x)
{
return x&-x;
}
void add(int x,int c)
{
int c1 = c*x;
while(x<=n)
{
tr1[x]+=c,tr2[x]+=c1;
x+=lowbit(x);
}
}
void ADD(int l,int r,int c)
{
add(l,c),add(r+1,-c);
}
int get_sum(int r,int *tr)
{
int res = 0;
while(r>0)
{
res+=tr[r];
r-=lowbit(r);
}
return res;
}
int GET(int l,int r)
{
return (r+1)*get_sum(r,tr1)-(l+1)*get_sum(l,tr1)-(get_sum(r,tr2)-get_sum(l,tr2));
}
void solve(){
int m ;
cin>>n>>m;
int old = 0;
for(int i = 0;i<n;i++)
{
int c ;
cin>>c;
add(i+1,c-old),old = c;
}
while(m--)
{
int op ;
cin>>op;
if(op == 1)
{
int x,y,c;
cin>>x>>y>>c;
ADD(x,y,c);
}
else{
int x;
cin>>x;
cout<<GET(x-1,x)<<endl;
}
}
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
P1966 [NOIP 2013 提高组] 火柴排队
题意:

思路:
想要得到一个最小距离,显然需要令两个序列在每一个位置上的数字的相对大小是一样的才行
例如:
1 3 2 4,
2 4 3 5
令两个序列进行排序,同时记录原有下标,再生成对应的映射关系。再求一次逆序对数量就行了。这个映射关系指的是两个序列在相对大小上的差异程度。
例如上面举的例子,经过上述操作之后就会形成;
1 2 3 4
1 2 3 4
的映射关系,显然不存在逆序对,既不需要额外移动。
/*
逆序对变有序,就是相邻元素移动距离之和。
本题得到一个目标序列(也就是两个数组排序之后映射得到的),然后求逆序对,也就是从当前序列变成原来的状态(没有映射之前的),也就相当于如何从原始状态到目标状态走的总步数。
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5+10;
const int INF = 0x3f3f3f3f;
const int mod = 1e8-3;
int tr[N];
int n;
struct node{
int v,idx;
};
bool cmp(node a,node b)
{
return a.v<b.v;
}
int lowbit(int x)
{
return x&-x;
}
void add(int x,int c)
{
while(x<=n)
{
tr[x]+=c;
x+=lowbit(x);
}
}
int get_sum(int r)
{
int res = 0;
while(r>0)
{
res+=tr[r];
r-=lowbit(r);
}
return res;
}
void solve(){
cin>>n;
vector<node>a(n+1),b(n+1);
for(int i = 1;i<=n;i++)
cin>>a[i].v,a[i].idx = i;
for(int i = 1;i<=n;i++)
cin>>b[i].v,b[i].idx = i;
vector<int>x(n+1);
sort(a.begin()+1,a.begin()+1+n,cmp);
sort(b.begin()+1,b.begin()+1+n,cmp);
for(int i = 1;i<=n;i++)
x[a[i].idx] = b[i].idx;
int ans = 0 ;
for(int i = n;i>=1;i--)
{
ans += get_sum(x[i]-1);
ans%=mod;
add(x[i],1);
}
cout<<ans<<endl;
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
P3605 [USACO17JAN] Promotion Counting P
题意:
思路:
DFS+树状数组+差分思想。
设ans[i]为节点i的答案,ans[i] = 更新完i的子树的后的总数-更新i前的总数
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+10;
const int INF = 0x3f3f3f3f;
int n;
vector<int>li,ans(N);
vector<int>tree[N];
int tr[N],siz[N];
int lowbit(int x)
{
return x&-x;
}
void add(int x,int c)
{
while(x<=n)
{
tr[x]+=c;
x+=lowbit(x);
}
}
int get_sum(int r)
{
int res = 0;
while(r>0)
{
res+=tr[r];
r-=lowbit(r);
}
return res;
}
void dfs(int u,int f){
ans[u]-=(get_sum(n)-get_sum(li[u]));
for(int i = 0;i<tree[u].size();i++)
{
int v = tree[u][i];
if(v == f)
continue;
dfs(v,u);
}
ans[u] += (get_sum(n)-get_sum(li[u]));
add(li[u],1);
}
void solve(){
cin>>n;
vector<int>arr(n+1),b(n+1);
for(int i = 1;i<=n;i++)
cin>>arr[i],b[i] = arr[i];
sort(arr.begin()+1,arr.begin()+n+1);
arr.erase(unique(arr.begin()+1,arr.begin()+1+n),arr.end());
for(int i = 1;i<=n;i++)
{
b[i] = lower_bound(arr.begin()+1,arr.end(),b[i])-arr.begin();
}
for(int i = 2;i<=n;i++)
{
int fa;
cin>>fa;
tree[i].push_back(fa);
tree[fa].push_back(i);
}
li = b;
dfs(1,0);
for(int i = 1;i<=n;i++)
cout<<ans[i]<<endl;
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}
P1972 [SDOI2009] HH的项链
题意:

思路:
对于某一段区间,我们显然只在乎所有元素最后出现的位置,所以当出现了一个之前出现过的贝壳时,将先前的贝壳删掉即可,这样维护一个树状数组就不会出现重复的问题。
举个例子:1 2 1 3 3
1没出现过,此时树状数组1 0 0 0 0
2没出现过,此时树状数组1 1 0 0 0
1出现过,此时0 1 1 0 0
3没出现过,此时0 1 1 1 0
3出现过,此时0 1 1 0 1
所以我们对离线处理询问,并对r值进行排序,需要注意的是会出现r值相同的情况。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10;
const int INF = 0x3f3f3f3f;
int tr[N];
int n;
struct Q{
int l,r,idx;
}q[N];
map<int,int>check;
void add(int x,int c);
void update(int ar,int pos)
{
if(check.count(ar))
{
add(check[ar],-1);
add(pos,1);
check[ar] = pos;
}
else
{
add(pos,1);
check[ar] = pos;
}
}
bool cmp(Q a,Q b)
{
return a.r<b.r;
}
int lowbit(int x)
{
return x&-x;
}
void add(int x,int c)
{
while(x<=n)
{
tr[x]+=c;
x+=lowbit(x);
}
}
int get_sum(int r)
{
int res = 0;
while(r>0)
{
res+=tr[r];
r-=lowbit(r);
}
return res;
}
void solve(){
cin>>n;
vector<int>arr(n+1);
for(int i = 1;i<=n;i++)
cin>>arr[i];
int m;
cin>>m;
for(int i = 1;i<=m;i++)
{
int l,r;
cin>>l>>r;
q[i] = {l,r,i};
}
vector<int>ans(m+1);
sort(q+1,q+1+m,cmp);
for(int i = 1,j = 1;i<=n;i++)
{
if(j == m+1)
break;
update(arr[i],i);
if(q[j].r == i)
{
while(q[j].r==i)
ans[q[j].idx] = get_sum(q[j].r)-get_sum(q[j].l-1),j++;
}
}
for(int i = 1;i<=m;i++)
cout<<ans[i]<<'\n';
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int T = 1;
//cin>>T;
while(T--)
{
solve();
}
}