基础算法模块总结

来源:互联网 发布:linux 日志切割脚本 编辑:程序博客网 时间:2024/06/04 00:49

并查集

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