基础算法模块总结


并查集

Problem 1
【bzoj1116】[POI2008]CLO
题意:把无向图的一部分边定向,让每个点有且仅有一个入度。
(无向边不参与计算。)
题解:
考虑一旦在加入一条边之后,形成环,那么这个集合显然是可行的。对并查集的每个集合设立标记,合并的时候标记合并即可。
标记合并:如果存在一个有标记,那么与这个集合合并的集合都是带标记的。
code:

//bzoj CLO
#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
using namespace std;
const int N = 1000001;
int fa[N],n,m;
bool vis[N];
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
int main()
{
    scanf("%d%d",&n,&m);
    Rep(i,n)fa[i] = i;
    int a,b;
    Rep(i,m)
    {
        scanf("%d%d",&a,&b);
        a = find(a),b = find(b);
        if(a != b)
        {
            fa[a] = b;
            vis[a] = vis[a] | vis[b];
            vis[b] = vis[a] | vis[b];
        }
        else vis[a] = 1;
    }
    Rep(i,n)if(!vis[i])return puts("NIE"),0;
    puts("TAK");
    return 0;
}

Problem 2
【bzoj1854】[Scoi2010]游戏
题意:
lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。 他最多能连续攻击boss多少次?
题解:
首先要考虑到,如果我们把(a,b)视作一条无向边的话。
那么考虑一个非树的联通块,显然这个联通块里每个都是可选的。
如果是树的话,那么我们肯定会选择前n - 1个值。
这样我们考虑,在合并两个点的时候,如果不在同一个联通块,那么肯定选小的,如果在同一个联通块,那么就把大的也标上。
Code:

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
using namespace std;
const int N = 1000001;
int fa[N],n;
bool vis[N];
int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
int main()
{
    scanf("%d",&n);
    int a,b,x,y;
    Rep(i,n)fa[i] = i;
    Rep(i,n)
    {
        scanf("%d%d",&a,&b);
        x = a,y = b;
        x = find(x),y = find(y);
        if(x != y)
        {
            if(x > y)swap(x,y);
            vis[x] = 1;
            fa[x] = y;
        }
        else vis[x] = vis[y] = 1;
    }
    Rep(i,n + 1)if(!vis[i])return printf("%d\n",i - 1),0;
    return 0;
}

Problem 3
【bzoj1529】[POI2005]ska Piggy banks
Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放到了某些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,问最少要打破多少个存钱罐.
题解:考虑如果是重建图的DAG的话,那么如果一个scc入度为0,我们就必须怼(dui)掉这个scc。
但是不需要Tarjan,因为这肯定是个环套树。
显然是环套树的话,那么我们直接统计环的个数就行了。
那么会发现,这个东西肯定是等于联通块个数的。
并查集维护一下就行了。

//1529
#include <cstdio>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
using namespace std;
const int N = 1000001;
int fa[N],n;
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
int main()
{
    scanf("%d",&n);
    Rep(i,n)fa[i] = i;
    int x;
    Rep(i,n)
    {
        scanf("%d",&x);
        if(find(i) != find(x))fa[find(i)] = find(x);
    }
    int ans = 0;
    Rep(i,n)if(fa[i] == i)ans ++;
    printf("%d\n",ans);
    return 0;
}

Problem 4
Tyvj 1460 旅行
A国有n座城市,每座城市都十分美,这使得A国的民众们非常喜欢旅行。然而,A国的交通十分落后,这里只有m条双向的道路,并且这些道路都十分崎岖,有的甚至还是山路,只能靠步行。通过每条道路的长度、泥泞程度等因素,我们给每条道路评估一个“崎岖度”,表示通过这条道路的不舒适程度。
从X城市经过若干条道路到达Y城市,我们称这次旅行的“代价”为所经过道路“崎岖度”的最大值。当然,如果从X城市到Y城市有多条路线,民众们会自觉选择“代价”最小的路线进行旅行。但是,A国的民众也是有脾气的,如果旅行的“代价”超过了他们的“忍耐度”,他们就不选择这个旅行了,甚至宁愿在家里宅着。
现在A国的国王想进行若干次询问:给定民众的“忍耐度”,问还有多少对城市(X,Y)会存在旅行?请你对国王的每次询问分别给出回答。
显然的是,我们按照边权排序,然后就可以扫描线解决了。
然而懒的写扫描线所以多个常数也无所谓了。。。QAQ

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
using namespace std;
const int N = 100001;
int n,m,Q,sz[N],fa[N];
long long cur[N];
int read(){int x = 0;char ch = getchar();while(ch > '9' || ch < '0')ch = getchar();while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar();return x;}
struct Edge{int x,y,w;}e[N << 1];
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
bool cmp(Edge a,Edge b){return a.w < b.w;}
#define mid (l + r >> 1)
int Bin(int x)
{
    int l = 1,r = n;
    while(l < r)
    {
        if(x >= e[mid].w)l = mid + 1;
        else r = mid;
    }
    return l;
}
int main()
{
    scanf("%d%d%d",&n,&m,&Q);
    Rep(i,n)fa[i] = i,sz[i] = 1;
    Rep(i,m)e[i].x = read(),e[i].y = read(),e[i].w = read();//scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
    sort(e + 1,e + 1 + n,cmp);
    long long ans = 0;
    Rep(i,m)
    {
        int x = find(e[i].x),y = find(e[i].y);
        if(x != y)
        {
            ans -= 1ll * sz[x] * (sz[x] - 1) / 2;
            ans -= 1ll * sz[y] * (sz[y] - 1) / 2;
            sz[x] += sz[y];
            ans += 1ll * sz[x] * (sz[x] - 1) / 2;
            fa[y] = x;
        }
        cur[i] = ans;
    }
    while(Q --)
    {
        int x;
        x = read();
        printf("%lld\n",cur[Bin(x) - 1]);
    }
    return 0;
}

Problem 5
hzwer的陨石


Tarjan算法部分见图论整理。
KMP算法正在填坑。
DP一类的基础题正在写。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值