头文件
#include
定义vector数组
## 定义int型数组
vector <int> a; ## a为空
vector <int> b(a); ## 用a定义b
vector <int> a(100) ; ## a有100个为0的元素
vector <int> a(100, 6); ## a有100个为6的元素
## 定义string型数组
vector <string> a(10," null"); ## 10个值为null的元素
vector <string> vec(10,"hello"); ## 10个值为hello的元素
vector <string> b(a. begin(), a. end()); ## b是a的复制
## 定义结构型数组
struct point { int x, y; };
vector <point> a;
## 定义二维数组
vector <point> a[maxn]; ## 行静态,列动态
访问方式
## 下标访问
cout << a[i] << endl;
## 指针访问,用于以下情况
cout << *a.begin() << endl;
cout << *a.end() << endl;
## 迭代器访问
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)
cout<<*it<<endl;
## 指针移动访问(最好别用,优先用迭代器,这个方法不知道合不合法,其实和下标访问区别不大)
vector<int> *p = &a;
for(int i = 0;i < a.size();i++)
cout << (*p)[i] << endl;
常规操作(一些自带的函数)
基础函数
a.push_back(); ## 尾部添加元素
a.size(); ## 返回元素个数大小
a.empty(); ## 返回是否为空,bool值
a.resize(); ## 改变数组大小,变小删除后面元素,变大自动补0
a.clear(); ## 清空
————————————————————————————————————————————————————————————————————————————————————————————————————
插入与删除
a.insert(a.begin() + i,k); ## 在下标为i的元素前插入k
a.insert(a.end(),k,m); ## 尾部插入k个m
a.pop_back(); ## 删除尾部元素
a.erase(a.begin() + i); ## 删除下标为i的元素(下标为2)
a.erase(a.begin() + i,a.begin() + j); ## 删除区间[i,j - 1]内的元素
————————————————————————————————————————————————————————————————————————————————————————————————————
翻转与排序
reverse(a.begin() + i,a.begin() + j); ## 翻转区间[i,j - 1]内的元素
sort(a.begin() + i,a.begin() + j); ## 对区间[i,j - 1]内的元素排序
可直接用的比较函数
less(降序)
greater(升序)
————————————————————————————————————————————————————————————————————————————————————————————————————
寻找
vector<int>::iterator result = find(L.begin( ) + i,L.begin( ) + j,m); ## 在区间[i,j - 1]内寻找m
cout << result - a.begin() << endl; ## 输出m值的下标位置
头文件
#include
定义String
string str ## 生成空串
string s = "12346789"; ## 直接赋值定义
string s(a) ## 以a定义s
string s(a,strbegin) ## 以a从strbegin到结尾的串定义s,且strbegin只能为下标,不能为地址
string s(a,strbegin,strlen) ## 以a从strbegin开始长度为strlen的串定义s,且strbegin只能为下标,不能为地址
string s(n,c) ## 生成num个c字符的字符串
访问方式
## 下标访问
cout << s[i] << endl;
## 指针访问,用于以下情况
cout << *s.begin() << endl;
cout << *s.end() << endl;
## 迭代器访问
string::iterator it;
for(it = s.begin();it != s.end();it++)
cout << *it << endl;
常规操作(一些自带的函数)
运算符的直接使用
+: 尾部添加(字符字符串皆可)
>: 直接比较大小(按字典循序,只能和字符串做比较)
<: 同上
>=: 同上
<=: 同上
————————————————————————————————————————————————————————————————————————————————————————————————————
基础函数
s.append(a); ## 将a添加到s尾部,a只能为字符串
s.length(); ## 返回s的长度(及大小)
s.size(); ## 返回s的大小(及长度)
s.empty(); ## 返回s是否为空,返回值为bool类型
————————————————————————————————————————————————————————————————————————————————————————————————————
插入、删除、替换
s.insert(s.begin() + i,a); ## 在下标为i的元素前添加a,a只能为字符
s.erase(s.begin() + i); ## 删除下标为i的元素
s.erase(s.begin() + i,s.begin() + j); ## 删除区间[i,j - 1]内的元素
s.replace(strbegin,strlen,a); ## 从下标strbegin开始长度为strlen的字符替换为a,a只能为字符串
————————————————————————————————————————————————————————————————————————————————————————————————————
翻转与排序
reverse(s.begin() + i,s.begin() + j); ## 翻转区间[i,j - 1]内的元素
sort(s.begin() + i,s.begin() + j); ## 对区间[i,j - 1]内的元素排序
可直接用的比较函数
less(降序)
greater(升序)
————————————————————————————————————————————————————————————————————————————————————————————————————
寻找与比较
s.find(a) ## 在s中寻找a(a可以为字符串也可以为字符),如果找到返回找到的第一个下标的值,不能则返回2^32 - 1
s.rfind(a) ## 在s的末尾开始找a,其他同上
s.find(a,strindex) ## 在s下标为strindex的地方开始找a
s.compare(a) ## 将s与a比较(字典循序),a只能为字符串,大于返回1,等于返回0,小于返回-1
头文件
#include
定义stack
stack <int> a;
访问方式
对于stack而言,中间的元素是无法访问的,只能通过a.top()访问栈顶元素
a.top();
常规操作(一些自带的函数)
基础函数
a.push(x); # 将x放到栈顶
a.pop(); # 删除栈顶元素
a.size(); # 返回栈内元素个数
a.empty(); # 返回栈是否为空
由于栈的使用对空间占用较大(深度大了不能及时释放),所以一下两点可以解决
1.在程序中调用大系统的栈,依赖于系统和编译器(不推荐)
2.手工写栈(没试过,但感觉数组可以实现,自己封装一下应该就ok)
头文件
#include
定义queue
queue <int> a;
访问方式
同理,对于queue而言,中间的元素是无法访问的,只能通过a.front()访问栈顶元素
a.front();
常规操作(一些自带的函数)
基础函数
a.push(x); # 将x放到队尾
a.pop(); # 删除队首元素
a.back(); # 返回队尾元素
a.size(); # 返回队列内元素个数
a.empty(); # 返回队列是否为空
关于优先队列内容与队列高度相似(除了访问队首要使用 a.top(),其他并无不同),关键在于如何定义排序规则,在下面的Struct 结构体与stl的结合中有相关赘述(只是板块分的不太好),此处就不再说了。
头文件
#include
定义set
set <int> a;
访问方式
迭代器访问
set <int> :: iterator it;
for(it = a.begin();it != a.end();it++){
cout << *it << endl;
}
常规操作(一些自带的函数)
基础函数
a.insert(x); # 插入x元素
a.erase(a.begin()); # 注意此处函数内要填迭代器,a.begin()实质上貌似就是迭代器
a.clear(); # 清空
a.empty();
a.size();
a.find(k); # 返回一个迭代器,指向键值k
a.lower_bound(k); # 返回一个迭代器,指向键值不小于k的第一个元素
a.upper_bound(k); # 返回一个迭代器,指向键值大于k的第一个元素
multiset为能容纳相同元素的集合,因不了解(用的少了当然不知道),暂不做记录
EX1:set_union(A.begin(),A.end(),B.begin(),B.end(),inserter( C1 , C1.begin() ) );前四个参数依次是第一的集合的头尾,第二个集合的头尾。第五个参数的意思是将集合A、B取合集后的结果存入集合C中。
EX2:set_union(A.begin(),A.end(),B.begin(),B.end(),ostream_iterator(cout," “));这里的第五个参数的意思是将A、B取合集后的结果直接输出,(cout," ")双引号里面是输出你想用来间隔集合元素的符号或是空格。
set里面有set_intersection(取集合交集)、set_union(取集合并集)、set_difference(取集合差集)、set_symmetric_difference(取集合对称差集)等函数。
头文件
#include
定义map
map <int,int> a;
赋值
a[x]=c;
访问方式
没研究出来,打扰了
}
常规操作(一些自带的函数)
基础函数
a.insert(x); # 存在这个东西,但写起来贼麻烦,直接用赋值即可
a.erase(a.begin()); # 注意此处函数内要填迭代器,a.begin()实质上貌似就是迭代器
a.clear(); # 清空
a.empty();
a.size();
a.find(k); # 返回一个迭代器,指向键值k 找不到就map::end()
multimap存在,但不知道怎么用,暂不整理
vector
自己写cmp函数
bool GreaterSort (Node a,Node b) { return (a.x>b.x); }
bool LessSort (Node a,Node b) { return (a.x<b.x); }
sort(a.begin(),a.end(),GreaterSort);
————————————————————————————————————————————————————————————————————————————————————————————————————
重载<运算符
struct node ## 内联写法
{
int x, y;
bool operator < (const node & o) const
{
return x<o.x;
}
};
priority_queue
调用默认排序函数
priority_queue<int,vector<int>,greater<int> >q1;
priority_queue<int,vector<int>,less<int> >q1;
————————————————————————————————————————————————————————————————————————————————————————————————————
自定义比较函数
struct cmp1{ 重载()运算符
bool operator()(node a,node b)
{
if(a.x == b.x) return a.y > b.y;
return a.x > a.x;
}
};
priority_queue<node,vector<node>,cmp1> q;
————————————————————————————————————————————————————————————————————————————————————————————————————
重载<运算符
struct node{
int x,y;
friend bool operator<(node a,node b) ## 必须为友元关系
{
return a.x>b.x;
}
};
priority_queue <node> q;
set
调用默认排序函数
set<int,greater<int> > Set;
set<int,less<int> > Set;
————————————————————————————————————————————————————————————————————————————————————————————————————
自定义比较函数
struct intcmp {
bool operator() (const int& a, const int& b) const{
return a > b;
}
};
struct strcmp
{
bool operator() (const string& a, const string& b) const {
return a < b;
}
};
set<int, intcmp> Set
————————————————————————————————————————————————————————————————————————————————————————————————————
重载<运算符
struct node
{
int x,y;
bool operator < (const node &s) const {
if(x!=s.x) return y < s.y;
else return x<s.x;
}
};
set <node> Set;
map
map也有自动排序的特性,但现目前来讲没有特别用到的地方,暂不整理
总结:
由于vector支持sort排序,大多是时候使用sort的自定义排序会较为便捷。
而set和priority_queue无法使用sort排序,个人角度而言,重载<运算符可能使用起来较为方便。
一些备注:
1.如不使用写比较函数的方式,重载的都是 < 运算符。
2.而使用比较函数的话,除了vector,都得重载 () 并将比较函数放入定义中。
3.比较值得注意的是,priority_queue的 < 运算符重载必须得写成友元。
线性筛(欧拉筛)
取出素数版本外加判断表
int p[maxn]; 保存素数
bool v[maxn]; 判断是否为素数,0为素,1反之
void p_table(int n){
memset(p,0,sizeof(p));
memset(v,0,sizeof(v));
v[1] = v[0] = 1;
for(int i = 2;i <= n;++i){
if(!v[i]) p[++p[0]] = i;
for(int j = 1;j <= p[0] && i*p[j] <= n;++j){
v[i*p[j]] = 1;
if(i % p[j] == 0) break;
}
}
return ;
}
单纯取出素数筛
int p[maxn];
void p_table(int n){
memset(p,0,sizeof(p));
for(int i = 2;i <= n;++i){
if(!p[i]) p[++p[0]] = i;
for(int j = 1;j <= p[0] && i*p[j] <= n;++j){
p[i*p[j]] = 1;
if(i % p[j] == 0) break;
}
}
return ;
}
区间筛
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll mod = 998244353;
ll prime[1000010],vis[1000010];
ll lr[1000010],sum[1000010];
void prime_table(){
memset(prime,0,sizeof(prime));
memset(vis,0,sizeof(vis));
for(int i = 2;i <= 1000005;i++){
if(!vis[i]){
prime[++prime[0]] = i;
}
for(int j = 1;j <= prime[0] && i*prime[j] <= 1000005;j++){
vis[i*prime[j]] = i;
if(i % prime[j] == 0) break;
}
}
return;
}
int main()
{
prime_table();
ll t;
cin >> t;
while(t--){
ll l,r,k;
cin >> l >> r >> k;
for(int i = 0;i <= r - l;i++){
lr[i] = l + i;
sum[i] = 1;
}
for(int i = 1;prime[i] <= sqrt(r);i++){
ll idx = (l + prime[i] - 1) / prime[i];
for(idx = idx*prime[i];idx <= r;idx += prime[i]){
ll cnt = 0;
while(lr[idx - l] % prime[i] == 0){
lr[idx - l] /= prime[i];
cnt++;
}
sum[idx - l] *= (cnt*k+1 % mod);
sum[idx - l] %= mod;
}
}
ll ans = 0;
for(int i = 0;i <= r-l;i++){
if(lr[i] != 1){
sum[i] *= (k + 1);
sum[i] %= mod;
}
ans += sum[i];
ans %= mod;
}
ans %= mod;
cout << ans << endl;
}
return 0;
}
区间筛模板2
#include
using namespace std;
typedef long long ll;
const int MAX = 1e5+10;
const int MAX_INTERVAL = 2e5+100;
bool is_prime[MAX_INTERVAL];
bool is_prime_small[MAX];
int segment_sieve(ll a,ll b){
for(int i = 0; (ll)i * i <= b; i++) is_prime_small[i] = true;
for(int i = 0; i <= b - a; i++) is_prime[i] = true;
for(int i = 2; (ll)i * i <= b; i++){
if(is_prime_small[i]){
for(int j = 2 * j; (ll)j * j <= b; j += i)
s_prime_small[j] = false;
for(ll j = max(2LL,(a+i-1)/i)*i; j <= b; j += i)
is_prime[j-a] = false;
//找到a-b范围内第一个i到倍数将其划去,记录到时候仍然记录离散化后的数组位置
}
}
int ans = 0;
for(int i = 0; i <= b-a; i++){
if(is_prime[i]) ans++;
}
return ans;
}
int main(){
int T,cas = 0;
scanf("%d",&T);
while(T--){
ll a,b;
scanf("%lld%lld",&a,&b);
int ans = segment_sieve(a,b);
if(a == 1) ans--;
printf("Case %d: %d\n",++cas,ans);
}
return 0;
}
大素数模板
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1000000 + 10;
#define INF 0x3f3f3f3f
#define clr(x,y) memset(x,y,sizeof x )
typedef long long ll;
#define eps 10e-10
const ll Mod = 1000000007;
typedef pair<ll, ll> P;
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll __int64
//****************************************************************
// Miller_Rabin 算法进行素数测试
//速度快,而且可以判断 <2^63的数
//****************************************************************
const int S=20;//随机算法判定次数,S越大,判错概率越小
//计算 (a*b)%c. a,b都是long long的数,直接相乘可能溢出的
/*
ll Mult_mod (ll a,ll b,ll c) // a,b,c <2^63
{
a%=c;
b%=c;
ll ret=0;
while (b)
{
if (b&1) {ret+=a;ret%=c;}
a<<=1;
if (a>=c)a%=c;
b>>=1;
}
return ret;
}*/
ll Mult_mod (ll a,ll b,ll c) //减法实现比取模速度快
{ //返回(a*b) mod c,a,b,c<2^63
a%=c;
b%=c;
ll ret=0;
while (b)
{
if (b&1)
{
ret+=a;
if (ret>=c) ret-=c;
}
a<<=1;
if (a>=c) a-=c;
b>>=1;
}
return ret;
}
//计算 x^n %c
ll Pow_mod (ll x,ll n,ll mod) //x^n%c
{
if (n==1) return x%mod;
x%=mod;
ll tmp=x;
ll ret=1;
while (n)
{
if (n&1) ret=Mult_mod(ret,tmp,mod);
tmp=Mult_mod(tmp,tmp,mod);
n>>=1;
}
return ret;
}
//以a为基,n-1=x*2^t a^(n-1)=1(mod n) 验证n是不是合数
//一定是合数返回true,不一定返回false
bool Check (ll a,ll n,ll x,ll t)
{
ll ret=Pow_mod(a,x,n);
ll last=ret;
for (int i=1;i<=t;i++)
{
ret=Mult_mod(ret,ret,n);
if(ret==1&&last!=1&&last!=n-1) return true; //合数
last=ret;
}
if (ret!=1) return true;
return false;
}
// Miller_Rabin()算法素数判定
//是素数返回true.(可能是伪素数,但概率极小)
//合数返回false;
bool Miller_Rabin (ll n)
{
if (n<2) return false;
if (n==2) return true;
if ((n&1)==0) return false;//偶数
ll x=n-1;
ll t=0;
while ((x&1)==0) {x>>=1;t++;}
for (int i=0;i<S;i++)
{
ll a=rand()%(n-1)+1; //rand()需要stdlib.h头文件
if (Check(a,n,x,t))
return false;//合数
}
return true;
}
//************************************************
//pollard_rho 算法进行质因数分解
//************************************************
ll factor[100];//质因数分解结果(刚返回时是无序的)
int tol;//质因数的个数。数组下标从0开始
ll Gcd (ll a,ll b)
{
if (a==0) return 1; //???????
if (a<0) return Gcd(-a,b);
while (b)
{
ll t=a%b;
a=b;
b=t;
}
return a;
}
ll Pollard_rho (ll x,ll c)
{
ll i=1,k=2;
ll x0=rand()%x;
ll y=x0;
while (true)
{
i++;
x0=(Mult_mod(x0,x0,x)+c)%x;
ll d=Gcd(y-x0,x);
if (d!=1 && d!=x) return d;
if (y==x0) return x;
if (i==k) {y=x0;k+=k;}
}
}
//对n进行素因子分解
void Findfac (ll n)
{
if (Miller_Rabin(n)) //素数
{
factor[tol++]=n;
return;
}
ll p=n;
while (p>=n) p=Pollard_rho(p,rand()%(n-1)+1);
Findfac(p);
Findfac(n/p);
}
int main () // Poj 1811 交G++ 比c++ 快很多
{
// srand(time(NULL));//需要time.h头文件 //POJ上G++要去掉这句话
int T;
scanf("%d",&T);
while (T--)
{
ll n;
scanf("%I64d",&n);
if (Miller_Rabin(n))
{
printf("Prime\n");
continue;
}
tol=0;
Findfac(n);
ll ans=factor[0];
for (int i=1;i<tol;i++)
if (factor[i]<ans)
ans=factor[i];
printf("%I64d\n",ans);
}
return 0;
裸版查找
int find(int k){
return pre[k] = pre[k] == k ?k :find(pre[k]);
}
带权更新查找
int find(int x){
if(pre[x] == x) return x;
int t = pre[x];
pre[x] = find(pre[x]);
ans[x] += ans[t];
return pre[x];
}
对立面并查集
#include
using namespace std;
int pre[100005],e[100005]; //e数组设置对立面
int find(int x){
return pre[x] = pre[x] == x ?x :find(pre[x]);
}
int main()
{
int t,n,m,x,y;
char k;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i) pre[i] = i,e[i] = 0;
for(int i = 0;i < m;++i){
getchar();
scanf("%c%d%d",&k,&x,&y);
if(k == 'D'){ //敌人的话设立对立面,并且之前要处理掉已经找出来的关系
if(e[x]) pre[find(e[x])] = pre[find(y)];
if(e[y]) pre[find(e[y])] = pre[find(x)];
e[x] = y;
e[y] = x;
}
else{
if(find(x) == find(e[y])) printf("In different gangs.\n");
else if(find(x) == find(y)) printf("In the same gang.\n");
else printf("Not sure yet.\n");
}
}
}
return 0;
}
poj-1703
三种及以上环形关系并查集(赋权)
#include
using namespace std;
const int maxn = 5e4 + 5;
int po[maxn];
int pre[maxn];
int find(int x){
if(pre[x] == x) return x;
int t = pre[x];
pre[x] = find(pre[x]);
po[x] = (po[x] + po[t]) % 3; //更新方式不同,0吃1,1吃2,2吃3
return pre[x];
}
int main()
{
int n,k,d,x,y,ans = 0;
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;++i) pre[i] = i,po[i] = 0;
for(int i = 0;i < k;++i){
scanf("%d%d%d",&d,&x,&y);
if(x > n || y > n || (d == 2 && x == y)) {ans++;continue;}
int xx = find(x);
int yy = find(y);
if(d == 1){
if(xx == yy && po[x] != po[y]) {ans++;continue;}
po[yy] = (po[x] - po[y] + 3) % 3;
pre[yy] = xx;
}
else{
if(xx == yy && (po[x]+1) % 3 != po[y]) {ans++;continue;}
po[yy] = (po[x] - po[y] + 4) % 3;
pre[yy] = xx;
}
}
printf("%d\n",ans);
return 0;
}
poj 1182
三种及以上环状关系(扩展域)
#include
using namespace std;
const int maxn = 15e4+5;
int pre[maxn];
int find(int x){
return pre[x] = pre[x] == x ?x :find(pre[x]);
}
int main()
{
int n,k,d,x,y,ans = 0;
scanf("%d%d",&n,&k);
for(int i = 1;i <= n*3;++i) pre[i] = i;
for(int i = 0;i < k;++i){
scanf("%d%d%d",&d,&x,&y);
if(x < 1 || y < 1 || x > n || y > n || (d == 2 && x == y)) {ans++;continue;}
if(d == 1){
if(pre[find(x)] == pre[find(y+n)] || pre[find(x)] == pre[find(y+2*n)]) {ans++;continue;}
pre[find(x)] = pre[find(y)];
pre[find(x+n)] = pre[find(y+n)];
pre[find(x+2*n)] = pre[find(y+2*n)];
}
else{
if(pre[find(x)] == pre[find(y)] || pre[find(x)] == pre[find(y+n)]) {ans++;continue;}
pre[find(x)] = pre[find(y+2*n)];
pre[find(x+n)] = pre[find(y)];
pre[find(x+2*n)] = pre[find(y+n)];
}
}
printf("%d\n",ans);
return 0;
}
poj 1182 即多倍数组
** 本质思想,维护根节点权值为0,边权转点权差**
#include
#include
using namespace std;
const int maxn = 2e5+5;
int pre[maxn],po[maxn];
void init(int n){
for(int i = 0;i <= n;++i) pre[i] = i,po[i] = 0;
return ;
}
int find(int x){
if(pre[x] == x) return x;
int t = pre[x];
pre[x] = find(pre[x]);
po[x] += po[t];
return pre[x];
}
int main()
{
int x,y,d,n,m,ans;
while(~scanf("%d%d",&n,&m)){
init(n);
ans = 0;
for(int i = 0;i < m;++i){
scanf("%d%d%d",&x,&y,&d);
int xx = find(x-1);
int yy = find(y);
if(xx != yy){
pre[yy] = xx;
po[yy] = po[x-1] + d - po[y];
}
else if(po[x-1] + d != po[y]) ans++;
}
printf("%d\n",ans);
}
return 0;
}
hdu 3038 How Many Answers Are Wrong
结点权值和集合权值
#include
using namespace std;
const int maxn = 1e5+5;
int pre[30005],h[30005],ans[30005]; //h为集合权值,集合高度;ans为结点权值,其下面结点高度
int find(int x){
if(pre[x] == x) return x;
int t = pre[x];
pre[x] = find(pre[x]);
ans[x] += ans[t];
return pre[x];
}
int main()
{
int x,y,p,m,t;
char k;
scanf("%d",&p);
for(int i = 1;i <= 30000;++i) pre[i] = i,h[i] = 1,ans[i] = 0;
for(int i = 0;i < p;++i){
cin >> k;
if(k == 'M'){
scanf("%d%d",&x,&y);
int xx = find(x);
int yy = find(y);
if(xx == yy) continue;
pre[xx] = yy;
ans[xx] = h[yy];
h[yy] += h[xx];
}
else{
scanf("%d",&x);
find(x);
printf("%d\n",ans[x]);
}
}
return 0;
}
poj 1988
** 集合带权变形**
#include
#include
using namespace std;
const int maxn = 1e5 + 5;
const int INF = 1e5+1;
set <int> s[maxn],e;
int ans[maxn];
int pre[maxn];
int find(int x){
return pre[x] = pre[x] == x ?x :find(pre[x]);
}
void init(){
set<int>::iterator it;
for(it = e.begin();it != e.end();it++){
s[*it].clear();
pre[*it] = *it;
}
return ;
}
int main()
{
int x,y,k,t,cnt = 0,len = 0;
scanf("%d",&t);
for(int i = 0;i <= maxn-5;++i) pre[i] = i;
for(int i = 0;i < t;++i){
scanf("%d%d%d",&x,&y,&k);
cnt++;
e.insert(x);e.insert(y);
x = find(x);y = find(y);
if(k){
if(x == y) continue;
if(s[x].count(y)) ans[len++] = cnt,cnt = 0,init();
else{
pre[x] = y;
for(set<int>::iterator it = s[x].begin();it != s[x].end();it++){
s[*it].erase(x);
s[*it].insert(y);
s[y].insert(*it);
}
}
}
else{
if(x == y) ans[len++] = cnt,cnt = 0,init();
else s[x].insert(y),s[y].insert(x);
}
}
printf("%d\n",len);
for(int i = 0;i < len;++i) printf("%d\n",ans[i]);
return 0;
}
百度之星(普通权值变非冲突合集(权值))
基础RMQ(区间最大最小)
int n,query;
int num[MAXN];
int F_Min[MAXN][20],F_Max[MAXN][20];
void Init()
{
for(int i = 1; i <= n; i++)
{
F_Min[i][0] = F_Max[i][0] = num[i];
}
for(int i = 1; (1<<i) <= n; i++) //按区间长度递增顺序递推
{
for(int j = 1; j+(1<<i)-1 <= n; j++) //区间起点
{
F_Max[j][i] = max(F_Max[j][i-1],F_Max[j+(1<<(i-1))][i-1]);
F_Min[j][i] = min(F_Min[j][i-1],F_Min[j+(1<<(i-1))][i-1]);
}
}
}
int Query_max(int l,int r)
{
int k = (int)(log(double(r-l+1))/log((double)2));
return max(F_Max[l][k], F_Max[r-(1<<k)+1][k]);
}
int Query_min(int l,int r)
{
int k = (int)(log(double(r-l+1))/log((double)2));
return min(F_Min[l][k], F_Min[r-(1<<k)+1][k]);
}
区间最频繁次数(只有模板,尚未理解
#include
#include
#include
using namespace std;
const int maxn = 100017;
int num[maxn], f[maxn], MAX[maxn][20];
int n;
int max(int a,int b)
{
return a>b ? a:b;
}
int rmq_max(int l,int r)
{
if(l > r)
return 0;
int k = log((double)(r-l+1))/log(2.0);
return max(MAX[l][k],MAX[r-(1<<k)+1][k]);
}
void init()
{
for(int i = 1; i <= n; i++)
{
MAX[i][0] = f[i];
}
int k = log((double)(n+1))/log(2.0);
for(int i = 1; i <= k; i++)
{
for(int j = 1; j+(1<<i)-1 <= n; j++)
{
MAX[j][i] = max(MAX[j][i-1],MAX[j+(1<<(i-1))][i-1]);
}
}
}
int main()
{
int a, b, q;
while(scanf("%d",&n) && n)
{
scanf("%d",&q);
for(int i = 1; i <= n; i++)
{
scanf("%d",&num[i]);
}
sort(num+1,num+n+1);
for(int i = 1; i <= n; i++)
{
if(i == 1)
{
f[i] = 1;
continue;
}
if(num[i] == num[i-1])
{
f[i] = f[i-1]+1;
}
else
{
f[i] = 1;
}
}
init();
for(int i = 1; i <= q; i++)
{
scanf("%d%d",&a,&b);
int t = a;
while(t<=b && num[t]==num[t-1])
{
t++;
}
int cnt = rmq_max(t,b);
int ans = max(t-a,cnt);
printf("%d\n",ans);
}
}
return 0;
}
**二维RMQ(O(nmlog(m))-O(n))
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAXN 250
int dp[MAXN][MAXN][20];
int dp1[MAXN][MAXN][20];
int a[MAXN][MAXN];
int n,m;
void st(){
for(int i=1;i<=n;i++)
for(int k=0;(1<<k)<=m;k++){
for(int j=1;j+(1<<k)-1<=m;j++){
if(k==0){
dp[i][j][k]=dp1[i][j][k]=a[i][j];
}
else {
dp[i][j][k]=max(dp[i][j][k-1],dp[i][j+(1<<(k-1))][k-1]);
dp1[i][j][k]=min(dp1[i][j][k-1],dp1[i][j+(1<<(k-1))][k-1]);
}
}
}
}
int rmq2dmax(int x,int y,int x1,int y1){
int k=log2(y1-y+1);
int mm=max(dp[x][y][k],dp[x][y1-(1<<k)+1][k]);
for(int i=x+1;i<=x1;i++)
mm=max(mm,max(dp[i][y][k],dp[i][y1-(1<<k)+1][k]));
return mm;
}
int rmq2dmin(int x,int y,int x1,int y1){
int k=log2(y1-y+1);
int mm=min(dp1[x][y][k],dp1[x][y1-(1<<k)+1][k]);
for(int i=x+1;i<=x1;i++)
mm=min(mm,min(dp1[i][y][k],dp1[i][y1-(1<<k)+1][k]));
return mm;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAXN 300
int rec[MAXN][MAXN];
int dp[MAXN][MAXN][10][10];
int dp1[MAXN][MAXN][10][10];
//dp-->max
//dp1-->min
int n,m;
inline int maxm(int a,int b,int c,int d){
if(a<b)a=b; if(a<c)a=c; if(a<d)a=d;
return a;
}
inline int minm(int a,int b,int c,int d){
if(b<a)a=b; if(c<a)a=c; if(d<a)a=d;
return a;
}
void st()
{
for(int k=0;(1<<k)<=n;k++)
for(int l=0;(1<<l)<=m;l++)
for(int i=1;i+(1<<k)-1<=n;i++)
for(int j=1;j+(1<<l)-1<=m;j++)
{
if(!k&&!l){
dp1[i][j][k][l]=dp[i][j][k][l]=rec[i][j];
}
else if(k==0){
dp[i][j][k][l]=max(dp[i][j][k][l-1],dp[i][j+(1<<(l-1))][k][l-1]);
dp1[i][j][k][l]=min(dp1[i][j][k][l-1],dp1[i][j+(1<<(l-1))][k][l-1]);
//printf("%d %d\n",dp[i][j][k][l-1],dp[i][j+(1<<(l-1))][k][l-1]);
}
else if(l==0){
dp[i][j][k][l]=max(dp[i][j][k-1][l],dp[i+(1<<(k-1))][j][k-1][l]);
dp1[i][j][k][l]=min(dp1[i][j][k-1][l],dp1[i+(1<<(k-1))][j][k-1][l]);
//printf("%d %d\n",dp[i][j][k-1][l],dp[i+(1<<(k-1))][j][k-1][l]);
}
else {
dp[i][j][k][l]=maxm(dp[i][j][k-1][l-1],dp[i+(1<<(k-1))][j][k-1][l-1],
dp[i][j+(1<<(l-1))][k-1][l-1],dp[i+(1<<(k-1))][j+(1<<(l-1))][k-1][l-1]);
dp1[i][j][k][l]=minm(dp1[i][j][k-1][l-1],dp1[i+(1<<(k-1))][j][k-1][l-1],
dp1[i][j+(1<<(l-1))][k-1][l-1],dp1[i+(1<<(k-1))][j+(1<<(l-1))][k-1][l-1]);
//printf("%d %d %d %d\n",dp[i][j][k-1][l-1],dp[i+(1<<(k-1))][j][k-1][l-1],
//dp[i][j+(1<<(l-1))][k-1][l-1],dp[i+(1<<(k-1))][j+(1<<(l-1))][k-1][l-1]);
}
//printf("i:%d j:%d k:%d l:%d dp:%d\n",i,j,k,l,dp[i][j][k][l]);
//system("pause");
}
}
int rmq2dmax(int x,int y,int x1,int y1){
//if(x==x1&&y==y1)return dp[x][y][0][0];
int k=log(x1-x+1)/log(2);
int l=log(y1-y+1)/log(2);
return maxm(dp[x][y][k][l],dp[x1-(1<<k)+1][y][k][l],
dp[x][y1-(1<<l)+1][k][l],dp[x1-(1<<k)+1][y1-(1<<l)+1][k][l]);
}
int rmq2dmin(int x,int y,int x1,int y1){
int k=log(x1-x+1)/log(2);
int l=log(y1-y+1)/log(2);
return minm(dp1[x][y][k][l],dp1[x1-(1<<k)+1][y][k][l],
dp1[x][y1-(1<<l)+1][k][l],dp1[x1-(1<<k)+1][y1-(1<<l)+1][k][l]);
}
直接求解欧拉函数
int euler(int n){ //返回euler(n)
int res=n,a=n;
for(int i=2;i*i<=a;i++){
if(a%i==0){
res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出
while(a%i==0) a/=i;
}
}
if(a>1) res=res/a*(a-1);
return res;
先筛选素数,然后实现欧拉函数打表
bool boo[50000];
int p[20000];
void prim()
{
memset(boo,0,sizeof(boo));
boo[0]=boo[1]=1;
int k=0;
for(int i=2; i<50000; i++)
{
if(!boo[i])
p[k++]=i;
for(int j=0; j<k&&i*p[j]<50000; j++)
{
boo[i*p[j]=1;
if(!(i%p[j]))
break;
}
}
}//筛选法打表
int phi(int n)
{
int rea=n;
for(int i=0; p[i]*p[i]<=n; i++)//对于一些不是素数的可不遍历
if(n%p[i]==0)
{
rea=rea-rea/n;
do
n/=p[i];
while(n%p[i]==0);
}
if(n>1)
rea=rea-rea/n;
return rea;
}
结合版
#include
using namespace std;
const int N = 1e6+10 ;
int phi[N], prime[N];
int tot;//tot计数,表示prime[N]中有多少质数
void Euler(){
phi[1] = 1;
for(int i = 2; i < N; i ++){
if(!phi[i]){
phi[i] = i-1;
prime[tot ++] = i;
}
for(int j = 0; j < tot && 1ll*i*prime[j] < N; j ++){
if(i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j]-1);
else{
phi[i * prime[j] ] = phi[i] * prime[j];
break;
}
}
}
}
递推求欧拉函数
for(i=1; i<=maxn; i++)
p[i]=i;
for(i=2; i<=maxn; i+=2)
p[i]/=2;
for(i=3; i<=maxn; i+=2)
if(p[i]==i)
{
for(j=i; j<=maxn; j+=i)
p[j]=p[j]/i*(i-1);
}
细节mod(解决mod选择问题)
ll Mod(ll x, ll mod){
return x < mod ? x : x % mod + mod;
}
super_log
#include
using namespace std;
typedef long long ll;
ll a, b, p;
ll Mod(ll x, ll mod)
{
return x < mod ? x : x % mod + mod;
}
ll euler_phi(ll n)
{
ll m = (ll)sqrt(n + 0.5);
ll ans = n;
for (ll i = 2; i <= m; i++)
{
if (n % i == 0)
{
ans = ans / i * (i - 1);
while (n % i == 0) n /= i; //除尽
}
}
if (n > 1) ans = ans / n * (n - 1); //剩下的不为1,也是素数
return ans;
}
ll qpow(ll a, ll b, ll p)
{
ll ret = 1;
while(b)
{
if(b&1) ret = Mod(ret * a, p);
a = Mod(a * a ,p);
b >>= 1;
}
return ret;
}
ll cal(ll a, ll b, ll p) //a^a^a..^a共b次
{
// printf("%lld %lld\n", t, p);
//if(t == 1) return Mod(a, p);
if(b == 0) return Mod(1, p);
if(p == 1) return Mod(a, p);
ll phip = euler_phi(p);
return qpow(a, cal(a, b-1, phip), p); //第一类和第三类
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%lld%lld%lld", &a, &b, &p);
printf("%lld\n", cal(a, b, p) % p); //这个取模不能少
}
return 0;
}
多重节是相同的,不同的情况下,可以通过数组记录操作
附上另一题
hdu 2837
#include
#include
#include
#include
#define int long long
using namespace std;
const int MAXN = 1e5 + 10, INF = 1e9 + 10;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int N, M, PhiM;
int fastpow(int a, int p, int mod) {
if(a == 0) return p == 0;
int base = 1;
while(p) {
if(p & 1) base = (base * a) % mod;
p >>= 1; a = (a * a) % mod;
}
return base == 0 ? mod : (base + mod)% mod;
}
int GetPhi(int x) {
int limit = sqrt(x), ans = x;
for(int i = 2; i <= limit; i++) {
if(!(x % i)) ans = ans / i * (i - 1) ;
while(!(x % i)) x /= i;
}
if(x > 1) ans = ans / x * (x - 1);
return ans;
}
int F(int N, int mod) {
if(N < 10) return N;
return fastpow((N % 10), F(N / 10, GetPhi(mod)), mod);
}
main() {
int QwQ = read();
while(QwQ--) {
N = read(); M = read();
printf("%I64d\n", F(N, M));
}
return 0;
}
nlogn
#include
using namespace std;
int main()
{
int s = 0,a[100005],dp[100005],dp2[100005];
memset(dp,0,sizeof(dp));
memset(dp2,0,sizeof(dp2));
int x;
while(~scanf("%d",&x)) a[++s] = x;
int len = 1,len2 = 1;
dp[1] = dp2[1] = a[1];
for(int i = 2;i <= s;++i){
if(a[i] > dp[len]) dp[++len] = a[i];
else{
int idx = lower_bound(dp+1,dp+len+1,a[i]) - dp;
dp[idx] = a[i];
}
if(a[i] <= dp2[len2]) dp2[++len2] = a[i];
else{
int idx = upper_bound(dp2+1,dp2+len2+1,a[i],greater<int>()) - dp2;
dp2[idx] = a[i];
}
}
printf("%d\n%d",len2,len);
return 0;
}
洛谷 导弹拦截
记录一道例题(双向区分dp):
#include
#include
#include
using namespace std;
string s;
inline bool comp(int l1,int r1,int l2,int r2){
while(s[l1] == '0' && l1 < r1) l1++;
while(s[l2] == '0' && l2 < r2) l2++;
string a = s.substr(l1,r1 - l1 + 1);
string b = s.substr(l2,r2 - l2 + 1);
// cout << l1 << ' ' << r1 << ' ' << l2 << ' ' << r2 << endl;
// cout << a << ' ' << b << endl;
if(a.size() < b.size()) return 1;
if(a.size() > b.size()) return 0;
if(b > a) return 1;
return 0;
}
int main()
{
while(cin >> s && s != "0"){
int dp[100],dp1[100];
memset(dp,0,sizeof(dp));
memset(dp1,0,sizeof(dp1));
dp[0] = 0;
int len = s.size() - 1;
for(int i = 1;i <= len;i++){
for(int j = i - 1;j >= 0;j--){
if(comp(dp[j],j,j + 1,i)){
dp[i] = j + 1;
break;
}
}
}
int i;
dp1[dp[len]] = len;
for(i = dp[len] - 1;i >= 0 && s[i] == '0';i--) dp1[i] = len;
for(;i >= 0;i--){
dp1[i] = len;
for(int j = dp[len] - 1;j >= i;j--){
if(comp(i,j,j+1,dp1[j+1])){
dp1[i] = j;
break;
}
}
}
int f = 0;
for(int i = 0;i <= len;i = dp1[i] + 1){
if(f) cout << ',';
f = 1;
for(int j = i;j <= dp1[i];j++) cout << s[j];
}
cout << endl;
}
return 0;
}
POJ - 1239
拓展欧几里得
LL exgcd(LL a,LL b,LL &x,LL &y)//扩展欧几里得算法
{
if(b==0)
{
x=1,y=0;
return a;
}
LL ret=exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
LL getInv(int a,int mod)//求a在mod下的逆元,不存在逆元返回-1
{
LL x,y;
LL d=exgcd(a,mod,x,y);
return d==1?(x%mod+mod)%mod:-1;
}
费马小定理求逆元
LL qkpow(LL a,LL p,LL mod)
{
LL t=1,tt=a%mod;
while(p)
{
if(p&1)t=t*tt%mod;
tt=tt*tt%mod;
p>>=1;
}
return t;
}
LL getInv(LL a,LL mod)
{
return qkpow(a,mod-2,mod);
}
递推求逆元
LL inv[mod+5];
void getInv(LL mod)
{
inv[1]=1;
for(int i=2;i<mod;i++)
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
递归求逆元
LL inv(LL i)
{
if(i==1)return 1;
return (mod-mod/i)*inv(mod%i)%mod;
}
割点
#include
#include
#include
using namespace std;
struct node{
int v, next;
}edge[4005];
int head[1005], num[1005], low[1005], ans[1005];
int n, m, no, index, root;
void add(int u, int v){
edge[no].v = v;
edge[no].next = head[u];
head[u] = no++;
}
void init(){
no = 1, index = 0, root = 1;
memset(head, -1, sizeof head);
memset(ans, 0, sizeof ans);
memset(num, 0, sizeof num);
}
void dfs(int cur, int father){
int child = 0;
++index;
num[cur] = index;
low[cur] = index;
int k = head[cur];
while(k != -1){
if(num[edge[k].v] == 0){
++child;
dfs(edge[k].v, cur);
low[cur] = min(low[cur], low[edge[k].v]);
if(cur != root && low[edge[k].v] >= num[cur]) ans[cur] = 1;
if(child == 2 && cur == root) ans[cur] = 1;
}
else if(edge[k].v != father){
low[cur] = min(low[cur], num[edge[k].v]);
}
k = edge[k].next;
}
}
int main(){
int i, u, v;
scanf("%d %d", &n, &m);
init();
for(i = 1; i <= m; ++i){
scanf("%d %d", &u, &v);
add(u, v);
add(v, u);
}
dfs(root, root);
for(i = 1; i <= n; ++i)
if(ans[i]) printf("%d ", i);
printf("\n");
return 0;
}
割边
#include
#include
#include
using namespace std;
struct node{
int v, next;
}edge[4005];
int head[1005], num[1005], low[1005], ans[1005];
int n, m, no, index, root;
void add(int u, int v){
edge[no].v = v;
edge[no].next = head[u];
head[u] = no++;
}
void init(){
no = 1, index = 0, root = 1;
memset(head, -1, sizeof head);
memset(ans, 0, sizeof ans);
memset(num, 0, sizeof num);
}
void dfs(int cur, int father){
++index;
num[cur] = index;
low[cur] = index;
int k = head[cur];
while(k != -1){
if(num[edge[k].v] == 0){
dfs(edge[k].v, cur);
low[cur] = min(low[cur], low[edge[k].v]);
if(low[edge[k].v] > num[cur])
printf("%d--%d\n", cur, edge[k].v);
}
else if(edge[k].v != father){
low[cur] = min(low[cur], num[edge[k].v]);
}
k = edge[k].next;
}
}
int main(){
int i, u, v;
scanf("%d %d", &n, &m);
init();
for(i = 1; i <= m; ++i){
scanf("%d %d", &u, &v);
add(u, v);
add(v, u);
}
dfs(root, root);
return 0;
}
去一点求割点联通块数
#include
#include
#include
#include
using namespace std;
const int maxn = 5e3+5;
vector <int> e[maxn];
int root,cnt,dfn[maxn],low[maxn],ss[maxn];
void init(int n){
for(int i = 0;i < n;++i) e[i].clear();
return ;
}
void init2(int n){
for(int i = 0;i < n;++i) ss[i] = 1,dfn[i] = low[i] = 0;
return ;
}
void tarjan(int u,int fa,int ffa){
dfn[u] = low[u] = ++cnt;
int len = e[u].size();
for(int i = 0;i < len;++i){
int v = e[u][i];
if(v == ffa) continue;
if(v == fa) continue;
if(!dfn[v]){
tarjan(v,u,ffa);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u]) ss[u]++;
}
else low[u] = min(low[u],dfn[v]);
}
return ;
}
int main()
{
int u,v,n,m;
while(~scanf("%d%d",&n,&m)){
init(n);
init2(n);
for(int i = 0;i < m;++i){
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
int ans = 0;
for(int i = 0;i < n;++i){
init2(n);
int sum = 0;
for(int j = 0;j < n;++j){
if(i == j) continue;
if(!dfn[j]){
sum++;
cnt = 0;
root = j;
tarjan(j,-1,i);
ss[root]--;
}
}
for(int j = 0;j < n;++j){
if(i != j) ans = max(ans,sum+ss[j]-1);
}
}
printf("%d\n",ans);
}
return 0;
}
求割点联通块数
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1e3+5;
struct edg{
int to,next;
} e[100010*2];
int tot,root,cnt,dfn[maxn],low[maxn],is[maxn],head[maxn];
void init(){
memset(head,0,sizeof(head));
e[0].next = e[0].to = 0;
for(int i = 1;i <= 1000;++i) is[i] = dfn[i] = low[i] = 0;
cnt = tot = 0;
return ;
}
void tarjan(int u,int fa){
int sum = 0;
dfn[u] = low[u] = ++cnt;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa) continue;
if(!dfn[v]){
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u]) is[u]++;
}
else low[u] = min(low[u],dfn[v]);
}
return ;
}
void slove(int n,int m){
bool f = 1;
for(int i = m;i <= n;++i){
if(!dfn[i]){
root = i;
tarjan(root,0);
is[root]--;
}
}
for(int i = m;i <= n;++i){
if(is[i] > 0) printf(" SPF node %d leaves %d subnets\n",i,is[i]+1),f = 0;
}
if(f) printf(" No SPF nodes\n");
return ;
}
void add(int u,int v){
e[++tot].to = v;
e[tot].next = head[u];
head[u] = tot;
return ;
}
int main()
{
int u,v,m = 0x3f3f3f3f,n = 0,cas = 0;
char k;
while(~scanf("%d",&u) && u){
init();
scanf("%d",&v);
add(u,v);
add(v,u);
m = min(m,min(u,v));
n = max(n,max(u,v));
while(~scanf("%d",&u) && u){
scanf("%d",&v);
add(u,v);
add(v,u);
m = min(m,min(u,v));
n = max(n,max(u,v));
}
if(cas) printf("\n");
printf("Network #%d\n",++cas);
slove(n,m);
}
return 0;
}
点双(染点)
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 5010;
int dfn[maxn], low[maxn], num, cnt, stack[maxn], top, root;
vector < int > g[maxn];
set < int > scc[maxn];
int fa[maxn], n, m, q;
int read()
{
int x = 0, ch = getchar(), v = 1;
while(!isdigit(ch)){
if(ch == '-')
v = -1;
ch = getchar();
}
while(isdigit(ch)){
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
}
return x * v;
}
int get(int x)
{
if(x == fa[x]) return x;
return fa[x] = get(fa[x]);
}
void merge(int x, int y)
{
fa[get(x)] = get(y);
}
void tarjan(int x)
{
dfn[x] = low[x] = ++num;
stack[++top] = x;
if(x == root && g[x].size() == 0){
scc[++cnt].insert(x);
return ;
}
for(int i = 0; i < g[x].size(); i++){
int v = g[x][i];
if(!dfn[v]){
tarjan(v);
low[x] = min(low[x], low[v]);
if(low[v] >= dfn[x]){
cnt++;
int z;
do{
z = stack[top--];
scc[cnt].insert(z);
}while(z != v);
scc[cnt].insert(x);
}
}else{
low[x] = min(low[x], dfn[v]);
}
}
}
int main()
{
int cas = 1;
while(~scanf("%d %d %d", &n, &m, &q)){
if(m == 0 && n == 0 && q == 0)
break;
printf("Case %d:\n", cas++);
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
num = cnt = top = 0;
for(int i = 1; i <= n; i++){
g[i].clear();
scc[i].clear();
fa[i] = i;
}
for(int i = 1; i <= m; i++){
int x = read(), y = read();
x++, y++;
g[x].push_back(y);
g[y].push_back(x);
merge(x, y);
}
for(int i = 1; i <= n; i++){
if(!dfn[i]){
root = i;
tarjan(i);
}
}
for(int i = 1; i <= q; i++){
int x = read(), y = read();
x++, y++;
if(get(x) != get(y)){
printf("zero\n");
}else{
int flg = 0;
for(int i = 1; i <= cnt; i++){
if(scc[i].find(x) != scc[i].end() && scc[i].find(y) != scc[i].end() && scc[i].size() >= 3)
flg = 1;
}
if(flg == 1)
printf("two or more\n");
else
printf("one\n");
}
}
}
return 0;
}
点双(染边)
#include
using namespace std;
const int maxn = 110;
const int maxm = 10010;
struct node
{
int u, v, next;
}edge[maxm], tp;
int n, m; //点数,边数
int head[maxn], no;
int add_bcc[maxn];//去掉该点之后能增加的bcc数目
int index; //时间戳
int yltd; //图的初始连通分量
int num[maxn], low[maxn];//时间戳和能回到的最早时间戳
int iscut[maxn];//是否为割点
int bccno[maxn], bcc_cnt; //bccno[i]表示i属于哪个bcc
stack<node> S; //存储bcc边
vector<int> bcc[maxn];
inline void init()
{
no = 0;
memset(head, -1, sizeof head);
}
inline void add(int u, int v)
{
edge[no].u = u; edge[no].v = v;
edge[no].next = head[u]; head[u] = no++;
edge[no].u = v; edge[no].v = u;
edge[no].next = head[v]; head[v] = no++;
}
inline void input()
{
int u, v;
for(int i = 1; i <= m; ++i)
{
scanf("%d %d", &u, &v);
add(u, v);
}
}
void tarjan(int cur, int father)
{
int child = 0;
num[cur] = low[cur] = ++index;
int k = head[cur];
while(k != -1)
{
int v = edge[k].v;
if(!num[v])
{
S.push(edge[k]);
++child;
tarjan(v, cur);
low[cur] = min(low[cur], low[v]);
if(low[v] >= num[cur])
//把更节点看做普通的节点,对根节点这个条件是一定满足的,
//可以实现把回溯到根节点剩下的出栈,其实这就是一个新的双连通分量
{
iscut[cur] = 1;
++add_bcc[cur];
++bcc_cnt;//准备把新的双连通分量加入bcc
bcc[bcc_cnt].clear();
while(true)
{
tp = S.top(); S.pop();
if(bccno[tp.u] != bcc_cnt)
{
bcc[bcc_cnt].push_back(tp.u);
bccno[tp.u] = bcc_cnt;
}
if(bccno[tp.v] != bcc_cnt)
{
bcc[bcc_cnt].push_back(tp.v);
bccno[tp.v] = bcc_cnt;
}
if(tp.u == edge[k].u && tp.v == edge[k].v) break;
}
}
}
else if(num[v] < num[cur] && edge[k].v != father)
{
//num[v] < num[cur]的判断是为了防止当前cur为割点,然后它刚访问的一个双连通分量里有一个较深的点
//访问过了。然后再从cur访问,如果不判断就会将这个点加入S,造成错误,见上图。
//可以看到时间戳走到6再次回溯到2时,还能通过2对2-4这条边进行一次尝试,不判断的话4会被加到S
S.push(edge[k]);
low[cur] = min(low[cur], num[v]);
}
k = edge[k].next;
}
if(father < 0)
{
//把根节点看做普通节点了,所以下面最后的特殊判断必需。
if(child > 1) iscut[cur] = 1, add_bcc[cur] = child-1;
else iscut[cur] = 0, add_bcc[cur] = 0;
}
}
void Find_Cut(int l, int r)
{
index = bcc_cnt = yltd = 0;
memset(add_bcc, 0, sizeof add_bcc);
memset(num, 0, sizeof num);
memset(iscut, 0, sizeof iscut);
memset(bccno, 0, sizeof bccno);
memset(low, 0, sizeof low);
for(int i = l; i <= r; ++i)
{
if(!num[i]) tarjan(i, -1), ++yltd;
}
}
void PutAll(int l, int r)
{
for(int i = l; i <= r; ++i)
{
if(iscut[i]) printf("%d是割点,", i);
printf("去掉点%d之后有%d个双连通分量\n", i, add_bcc[i]+yltd);
}
}
void PutBcc()
{
printf("有%d个BCC\n", bcc_cnt);
for(int i = 1; i <= bcc_cnt; ++i)
{
printf("BCC%d有%d个点: ", i, bcc[i].size());
for(int j = 0; j < bcc[i].size(); ++j) printf("%d ", bcc[i][j]);
printf("\n");
}
}
int main()
{
while(~scanf("%d %d", &n, &m))
{
init();
input();
Find_Cut(1, n);
PutAll(1, n);
PutBcc();
}
return 0;
}
点双(染边加记录桥的数量)
#include
#include
#include
using namespace std;
const int maxn = 2e5+5;
struct edge{
int u,v;
edge(int u = 0,int v = 0):u(u),v(v){}
};
edge stk[maxn];
vector <int> e[maxn],bel[maxn],bcc[maxn];
int bridge,ans,color,top,cnt,dfn[maxn],low[maxn],bccno[maxn];
void init(int n){
ans = bridge = color = cnt = top = 0;
for(int i = 0;i <= n;++i){
bccno[i] = dfn[i] = low[i] = 0;
e[i].clear();
bel[i].clear();
}
return ;
}
void col(edge t){
int sum = 0;
color++;bcc[color].clear();
while(top >= 0){
edge x = stk[top--];
sum++;
if(bccno[x.u] != color){
bcc[color].push_back(x.u),bccno[x.u] = color;
bel[x.u].push_back(color);
}
if(bccno[x.v] != color){
bcc[color].push_back(x.v),bccno[x.v] = color;
bel[x.v].push_back(color);
}
if(x.u == t.u && x.v == t.v) break;
}
if(sum > bcc[color].size()) ans += sum;
return ;
}
void tarjan(int u,int fa){
dfn[u] = low[u] = ++cnt;
int len = e[u].size();
for(int i = 0;i < len;++i){
int v = e[u][i];
if(v == fa) continue;
edge temp(u,v);
if(!dfn[v]){
stk[++top] = temp;
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u]){
if(low[v] > dfn[u]) bridge++;
col(temp);
}
}
else if(dfn[u] > dfn[v]){
stk[++top] = temp;
low[u] = min(low[u],dfn[v]);
}
}
return ;
}
int main()
{
int q,n,m,u,v,cas = 0;
while(~scanf("%d%d",&n,&m) && n+m){
init(n);
while(m--){
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
for(int i = 0;i < n;++i) if(!dfn[i]) tarjan(i,-1);
printf("%d %d\n",bridge,ans);
}
return 0;
}
边双(找边)
#include
using namespace std;
const int maxn = 110;
const int maxm = 10010;
struct node
{
int u, v, next;
}edge[maxm];
int n, m; //点数,边数
int head[maxn], no;
int index; //时间戳
int num[maxn], low[maxn];//时间戳和能回到的最早时间戳
int iscutedge[maxm];//是否为割边,存邻接表的索引
inline void init()
{
no = 0;
memset(head, -1, sizeof head);
}
inline void add(int u, int v)
{
edge[no].u = u; edge[no].v = v;
edge[no].next = head[u]; head[u] = no++;
edge[no].u = v; edge[no].v = u;
edge[no].next = head[v]; head[v] = no++;
}
inline void input()
{
int u, v;
for(int i = 1; i <= m; ++i)
{
scanf("%d %d", &u, &v);
add(u, v);
}
}
void tarjan(int cur, int father)
{
num[cur] = low[cur] = ++index;
int k = head[cur];
while(k != -1)
{
int v = edge[k].v;
if(!num[v])
{
tarjan(v, cur);
low[cur] = min(low[cur], low[v]);
if(low[v] > num[cur])
{
//把割边的两个方向的边都标记
iscutedge[k] = iscutedge[k^1] = 1;
}
}
else if(edge[k].v != father)
{
low[cur] = min(low[cur], num[v]);
}
k = edge[k].next;
}
}
//找出割边标记上
void Find_CutEdge(int l, int r)
{
index = 0;
memset(iscutedge, 0, sizeof iscutedge);
memset(num, 0, sizeof num);
memset(low, 0, sizeof low);
for(int i = l; i <= r; ++i)
{
if(!num[i]) tarjan(i, -1);
}
}
int dfs(int cur)
{
num[cur] = 1;
int flag = 0; //判断是否存在边双联通分量,以免多输出换行
for(int k = head[cur]; k != -1; k = edge[k].next)
{
if(iscutedge[k]) continue;
flag = 1;
iscutedge[k] = iscutedge[k^1] = 1;
printf("(%d, %d) ", cur, edge[k].v);
if(!num[edge[k].v]) dfs(edge[k].v);
}
return flag;
}
//dfs输出就能得到相应的双连通分量
void PutBccEdge(int l, int r)
{
memset(num, 0, sizeof num);
printf("双连通分量的边有:\n");
for(int i = l; i <= r; i++)
if(!num[i])
{
if(dfs(i)) cout << endl;
}
}
int main()
{
while(~scanf("%d %d", &n, &m))
{
init();
input();
Find_CutEdge(1, n);
PutBccEdge(1, n);
}
return 0;
}
边双(染点)
#include
using namespace std;
const int maxn = 25005;
const int maxm = 1e5+5;
struct node {
int u, v, next;
} edge[maxm];
int no, head[maxn];
int idx, dfn[maxn], low[maxn];
int top, S[maxn];
int bcc_cnt, cut;
int bccno[maxn];
vector<int> bcc[maxn];
int n, m;
void init()
{
no = 0;
memset(head, -1, sizeof head);
}
void add(int u, int v)
{
edge[no].u = u; edge[no].v = v;
edge[no].next = head[u]; head[u] = no++;
}
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++idx;
S[++top] = u;
for(int k = head[u]; k+1; k = edge[k].next)
{
int v = edge[k].v;
if(!dfn[v])
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v] > dfn[u])
{
++cut; //割边+1
}
}
else if(v != fa)
{
low[u] = min(low[u], dfn[v]);
}
}
if(dfn[u] == low[u])
{
++bcc_cnt; //边双连通分量+1
do
{
bcc[bcc_cnt].push_back(S[top]);
bccno[S[top]] = bcc_cnt;
--top;
}
while(S[top+1] != u);
}
}
void work()
{
memset(dfn, 0, sizeof dfn);
memset(bccno, 0, sizeof bccno);
idx = top = bcc_cnt = cut = 0;
for(int i = 1; i <= n; ++i)
if(!dfn[i]) tarjan(i, i);
for(int i = 1; i <= bcc_cnt; ++i)
{
cout << i << ": ";
for(int j = 0; j < bcc[i].size(); ++j)
cout << bcc[i][j] << " ";
cout << endl;
}
}
int main()
{
init();
cin >> n >> m;
for(int i = 1; i <= m; ++i)
{
int u, v;
cin >> u >> v;
add(u, v); add(v, u);
}
work();
return 0;
}
割边缩点后每个点的度数
#include
using namespace std;
int n,m,k,len,tot,cnt,top,ans;
const int N=10005;
int dis[N],last[N*2],s[N],low[N],_stack[N],dfn[N],father[N],_color[N*2],vis[N*2];
struct ss
{
int to,next,u;
}e[N*2];
void insert(int x,int y)
{
e[++len].next=last[x];
e[len].to=y;
e[len].u=x;
last[x]=len;
}
void tarjan(int x,int fa)
{
int i=last[x];
for(dfn[x]=low[x]=++tot,_stack[++cnt]=x,s[x]=1;i;i=e[i].next)
{
int v=e[i].to;
if(father[i]==fa)continue;
if(!dfn[v])
{
tarjan(v,father[i]);
low[x]=min(low[x],low[v]);
}
else if(s[v])low[x]=min(dfn[v],low[x]);
}
if(low[x]>=dfn[x])
{
++top;
int t=-1;
do{
t=_stack[cnt--];
s[t]=0;
_color[t]=top;
}while(t!=x);
}
}
int main()
{
freopen("rpaths.in","r",stdin);
freopen("rpaths.out","w",stdout);
int i=0;
for(scanf("%d%d",&n,&m);++i<=m;)
{
int x,y;
scanf("%d%d",&x,&y);
insert(x,y);
father[len]=++cnt;
insert(y,x);
father[len]=cnt;
}
for(cnt=0,i=0;++i<=n;)if(!dfn[i])tarjan(i,0);
for(i=0;++i<=len;)
{
int x=e[i].u,v=e[i].to;
if(_color[x]==_color[v])continue;
++dis[_color[x]];
++dis[_color[v]];
}
for(i=0;++i<=top;)
if(dis[i]==2)ans++;
printf("%d",(ans+1)/2);
return 0;
}
割边缩点后求点的度数
#include
#include
#include
#include
using namespace std;
const int maxn=1000+10;
int n,m;
vector<int> G[maxn];
int dfs_clock;
int pre[maxn],low[maxn];
int degree[maxn];
int tarjan(int u,int fa)
{
int lowu=pre[u]=++dfs_clock;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(v==fa) continue;
if(!pre[v])
{
int lowv=tarjan(v,u);
lowu=min(lowu,lowv);
}
else if(pre[v]<pre[u])
lowu=min(lowu,pre[v]);
}
return low[u]=lowu;
}
int main()
{
scanf("%d%d",&n,&m);
dfs_clock=0;
memset(pre,0,sizeof(pre));
memset(degree,0,sizeof(degree));
for(int i=1;i<=n;i++) G[i].clear();
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
tarjan(1,-1);//得出所有节点的low值,每个不同的low值代表一个边双连通分量
for(int u=1;u<=n;u++)//遍历每条边
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(low[u]!=low[v]) degree[low[v]]++;
}
int cnt=0;
for(int i=1;i<=n;i++)if(degree[i]==1)
cnt++;
printf("%d\n",(cnt+1)/2 );
}
割边缩点图上跑lca(扩展)
#include"cstdio"
#include"cstring"
#include"cstdlib"
#include"cmath"
#include"string"
#include"map"
#include"cstring"
#include"iostream"
#include"algorithm"
#include"queue"
#include"stack"
#define inf 0x3f3f3f3f
#define M 100009
#define eps 1e-8
#define INT int
using namespace std;
struct node
{
int u,v,next;
}edge[M*10],e[M*10];
stack<int>q;
int t,head[M],dfn[M],low[M],indx,cut[M*10],num,cnt,belong[M],use[M],suo[M],mark[M*10],pre[M],pp[M],ranks[M],ans;
void init()
{
t=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v)
{
edge[t].u=u;
edge[t].v=v;
edge[t].next=head[u];
head[u]=t++;
}
void tarjan(int u,int id)//求桥
{
dfn[u]=low[u]=++indx;
q.push(u);
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(id==(i^1))continue;
if(!dfn[v])
{
tarjan(v,i);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])
{
cut[++num]=i;
mark[i]=mark[i^1]=1;//存桥的编号,且把其进行标记
}
}
else
low[u]=min(low[u],dfn[v]);
}
}
void slove(int n)
{
num=indx=0;
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(cut,0,sizeof(cut));
memset(mark,0,sizeof(mark));
for(int i=1;i<=n;i++)
{
if(!dfn[i])
tarjan(i,-1);
}
return ;
}
void dfs(int u,int tep)
{
use[u]=1;
suo[u]=tep;
for(int i=head[u];~i;i=edge[i].next)
{
if(mark[i])continue;
int v=edge[i].v;
if(!use[v])
dfs(v,tep);
}
}
void DFS(int u,int fa)
{
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(fa==v)continue;
pre[v]=u;
ranks[v]=ranks[u]+1;
DFS(v,u);
}
}
void LCA(int u,int v)
{
while(u!=v)
{
if(ranks[u]>ranks[v])
{
if(!pp[u])
{
ans++;
pp[u]=1;
}
u=pre[u];
}
else
{
if(!pp[v])
{
pp[v]=1;
ans++;
}
v=pre[v];
}
}
}
void litter(int n)
{
cnt=0;
memset(suo,0,sizeof(suo));
memset(use,0,sizeof(use));
for(int i=1;i<=num;i++)//缩点
{
int u=edge[cut[i]].u;
if(!suo[u])
{
dfs(u,++cnt);
}
int v=edge[cut[i]].v;
if(!suo[v])
dfs(v,++cnt);
e[i].u=u;
e[i].v=v;
}
// for(int i=1;i<=n;i++)
//printf("%d %d\n",i,suo[i]);
init();
for(int i=1;i<=num;i++)
{
int u=e[i].u;
int v=e[i].v;
add(suo[u],suo[v]);
add(suo[v],suo[u]);
}//把缩点后的图建成一棵树
memset(pre,-1,sizeof(pre));
ranks[1]=1;
DFS(1,1);
int Q;
cin>>Q;
int sum=0;
memset(pp,0,sizeof(pp));
while(Q--)
{
int a,b;
scanf("%d%d",&a,&b);
ans=0;
LCA(suo[a],suo[b]);
sum+=ans;
printf("%d\n",num-sum);
}
printf("\n");
}
int main()
{
int n,m,a,b,kk=1;
while(scanf("%d%d",&n,&m),n||m)
{
init();
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
slove(n);
printf("Case %d:\n",kk++);
litter(n);
}
return 0;
}
network
自己写的非缩点跑法
#include
#include
using namespace std;
const int maxn = 1e5+5;
vector <int> e[maxn];
int ans,cnt,dfn[maxn],low[maxn],p[maxn],pre[maxn];
bool is[maxn];
void init(int n){
cnt = ans = 0;
for(int i = 1;i <= n;++i){
is[i] = dfn[i] = low[i] = 0;
e[i].clear();
pre[i] = i;
}
return ;
}
void tarjan(int u,int fa){
dfn[u] = low[u] = ++cnt;
int len = e[u].size();
for(int i = 0;i < len;++i){
int v = e[u][i];
if(!dfn[v]){
pre[v] = u;
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] > dfn[u]) ans++,is[v] = 1;
}
else if(v != fa) low[u] = min(low[u],dfn[v]);
}
}
void lca(int u,int v){
if(dfn[u] < dfn[v]) swap(u,v);
while(dfn[u] > dfn[v]){
if(is[u]) ans--,is[u] = 0;
u = pre[u];
}
while(u != v){
if(is[u]) ans--,is[u] = 0;
if(is[v]) ans--,is[v] = 0;
u = pre[u],v = pre[v];
}
return ;
}
int main()
{
int q,n,m,u,v,cas = 0;
while(~scanf("%d%d",&n,&m) && n+m){
init(n);
while(m--){
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
tarjan(1,0);
printf("Case %d:\n",++cas);
scanf("%d",&q);
while(q--){
scanf("%d%d",&u,&v);
lca(u,v);
printf("%d\n",ans);
}
printf("\n");
}
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 100010;
int f[maxn], ans, n, m, dfn[maxn], low[maxn], num, cnt, fa[maxn], c[maxn];
vector < int > g[maxn];
int read()
{
int x = 0, ch = getchar(), v = 1;
while(!isdigit(ch)){
if(ch == '-')
v = -1;
ch = getchar();
}
while(isdigit(ch)){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * v;
}
int get(int x)
{
if(x == fa[x]) return x;
return fa[x] = get(fa[x]);
}
void merge(int x, int y)
{
fa[get(y)] = get(x);
}
void tarjan(int x, int fa)
{
dfn[x] = low[x] = ++num;
f[x] = fa;
for(int i = 0; i < g[x].size(); i++){
int v = g[x][i];
if(!dfn[v]){
tarjan(v, x);
low[x] = min(low[x], low[v]);
if(low[v] > dfn[x]){
ans++;
}else{
merge(v, x);
}
}else if(v != fa){
low[x] = min(low[x], dfn[v]);
}
}
}
void func(int x)
{
int fx = get(x);
int fy = get(f[x]);
if(fy != fx){
ans--;
fa[fx] = fy;
}
}
void lca(int x, int y)
{
while(dfn[x] > dfn[y]){
func(x);
x = f[x];
}
while(dfn[y] > dfn[x]){
func(y);
y = f[y];
}
while(x != y){
func(x);
func(y);
x = f[x];
y = f[y];
}
}
int main()
{
int cas = 1;
while(cin >> n >> m && n && m){
for(int i = 1; i <= n; i++){
fa[i] = i;
g[i].clear();
}
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(c, 0, sizeof(c));
memset(f, 0, sizeof(f));
num = cnt = ans = 0;
for(int i = 1; i <= m; i++){
int x = read(), y = read();
g[x].push_back(y);
g[y].push_back(x);
}
tarjan(1, 1);
printf("Case %d:\n", cas++);
int q = read();
for(int i = 1; i <= q; i++){
int x = read(), y = read();
if(get(x) != get(y))
lca(x, y);
printf("%d\n", ans);
}
printf("\n");
}
return 0;
}
基础倍增法
#include
using namespace std;
const int maxn = 5e5+10;
struct edg{
int to,next;
} e[maxn << 1];
int head[maxn],dep[maxn],p[maxn][20];
int tot;
void init(int n){
tot = 0;
e[0].to = 0;e[0].next = 0;
for(int i = 1;i <= n;++i) head[i] = 0,dep[i] = 0;
memset(p,0,sizeof(p));
return ;
}
void add(int u,int v){
e[++tot].to = v;
e[tot].next = head[u];
head[u] = tot;
return ;
}
void dfs(int u){
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(!dep[v]){
p[v][0] = u;
dep[v] = dep[u]+1;
dfs(v);
}
}
return ;
}
void change(int n){
for(int i = 1;(1 << i) <= n;++i){
for(int j = 1;j <= n;++j){
if(p[j][i-1]) p[j][i] = p[p[j][i-1]][i-1];
}
}
return ;
}
int lca(int u,int v){
if(dep[u] > dep[v]) swap(u,v);
int len = dep[v] - dep[u];
for(int i = len,j = 0;i;i >>= 1,++j){
if(i & 1) v = p[v][j];
}
if(u == v) return u;
for(int i = 19;i >= 0;--i){
if(p[u][i] == p[v][i]) continue;
u = p[u][i];
v = p[v][i];
}
return p[u][0];
}
int main(){
//std::ios::sync_with_stdio(false);
int u,v,n,m,s;
scanf("%d%d%d",&n,&m,&s);
init(n);
for(int i = 1;i < n;++i){
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dep[s] = 1;
dfs(s);
change(n);
for(int i = 0;i < m;++i){
scanf("%d%d",&u,&v);
printf("%d\n",lca(u,v));
}
return 0;
}
预处理与深度遍历结合
#include
#include
#include
#include
#include
#define num ch-'0'
#define rep(i,k,n) for(int i=k;i<=n;++i)
using namespace std;
int n,m,s,cnt=0;
int head[1000005],dep[1000005],fa[1000005][64],lg[1000005];
inline void get(int &res)
{
char ch;bool flag=0;
while(!isdigit(ch=getchar()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getchar());res=res*10+num);
(flag)&&(res=-res);
}
struct node
{
int to,nex;
}e[1000005];
inline void add(int x,int y)
{
e[++cnt].nex=head[x];
e[cnt].to=y;
head[x]=cnt;
}
void init(int u,int f)
{
dep[u]=dep[f]+1;
fa[u][0]=f;
for(int i=1;(1<<i)<=dep[u];++i)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(int i=head[u];i;i=e[i].nex)
{
if(e[i].to==f) continue;
init(e[i].to,u);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y])
x=fa[x][lg[dep[x]-dep[y]]-1];
if(x!=y)
{
for(int i=lg[dep[x]];i>=0;--i)
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
x=fa[x][0];
}
return x;
}
int main()
{
get(n),get(m),get(s);
rep(i,1,n-1)
{
int x,y;
get(x),get(y);
add(x,y);add(y,x);
}
init(s,0);
rep(i,1,n) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
rep(i,1,m)
{
int x,y;
get(x),get(y);
printf("%d\n",lca(x,y));
}
return 0;
}
边带权LCA
#include
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e4+5;
struct edg{
int from,to,dis,next;
} e[maxn*10],te[maxn*10];
int dep[maxn],head[maxn],pre[maxn],p[maxn][20],d[maxn][20];
int tot;
void init(int n){
tot = 0;
e[0].to = 0;e[0].next = 0;
te[0].to = 0;te[0].next = 0;
for(int i = 1;i <= n;++i) head[i] = 0,dep[i] = 0,pre[i] = i;
memset(p,0,sizeof(p));
memset(d,INF,sizeof(d));
return ;
}
void add(int u,int v,int d){
te[++tot].to = v;
te[tot].next = head[u];
te[tot].dis = d;
head[u] = tot;
return ;
}
void dfs(int u){
for(int i = head[u];i;i = te[i].next){
int v = te[i].to;
if(!dep[v]){
dep[v] = dep[u]+1;
p[v][0] = u;
d[v][0] = te[i].dis;
dfs(v);
}
}
return ;
}
bool comp(edg x,edg y){
return x.dis > y.dis;
}
int find(int x){
return pre[x] = pre[x] == x ?x :find(pre[x]);
}
void kal(int n){
sort(e+1,e+n+1,comp);
for(int i = 1;i <= n;++i){
int u = e[i].from;
int v = e[i].to;
if(pre[find(u)] == pre[find(v)]) continue;
pre[find(u)] = pre[find(v)];
//cout << "111 " << u << " " << v << " " << e[i].dis << endl;
add(u,v,e[i].dis);
add(v,u,e[i].dis);
}
return ;
}
void change(int n){
for(int i = 1;(1 << i) <= n;++i){
for(int j = 1;j <= n;++j){
if(p[j][i-1]){
p[j][i] = p[p[j][i-1]][i-1];
d[j][i] = min(d[j][i-1],d[p[j][i-1]][i-1]);
}
}
}
return ;
}
int lca(int u,int v){
int ans = INF;
if(dep[u] > dep[v]) swap(u,v);
int len = dep[v] - dep[u];
//cout << "len = " << len << endl;
for(int i = len,j = 0;i;i >>= 1,++j){
//cout << v << " " << j << " " << d[v][j] << endl;
if(i & 1) ans = min(ans,d[v][j]),v = p[v][j];
}
if(u == v) return ans;
for(int i = 19;i >= 0;--i){
//cout << p[u][i] << " " << p[v][i] << endl;
if(p[u][i] == p[v][i]) continue;
ans = min(ans,min(d[u][i],d[v][i]));
u = p[u][i];
v = p[v][i];
}
ans = min(ans,min(d[u][0],d[v][0]));
return ans;
}
int main(){
int u,v,n,m,q;
scanf("%d%d",&n,&m);
init(n);
for(int i = 1;i <= m;++i)
scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].dis);
kal(m);
for(int i = 1;i <= n;++i){
if(pre[find(i)] == i){
dep[i] = 1;
dfs(i);
}
}
change(n);
scanf("%d",&q);
while(q--){
scanf("%d%d",&u,&v);
if(pre[find(u)] == pre[find(v)]) printf("%d\n",lca(u,v));
else printf("-1\n");
}
return 0;
}
LCA转RMQ
int head[maxn];
int first[maxn];//首次出现的下标
int dp[maxn*2][30];
bool vis[maxn];
bool ok[maxn];
int deep[maxn*2];//深度数组
int ver[maxn*2];//节点序列
int n,cnt,tot;
struct Edge{
int next;
int to;
}edge[maxn];
void addedge(int u, int v){
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void dfs(int u, int step){
vis[u] = true;
ver[tot] = u;
first[u] = tot;
deep[tot++] = step;
for(int i=head[u]; i!=-1; i=edge[i].next){
if(!vis[edge[i].to]){
dfs(edge[i].to,step+1);
ver[tot] = u;
deep[tot++] = step;
}
}
}
void st(int n){
for(int i=0; i<n; i++)
dp[i][0] = i;//保存的下标
for(int j=1; (1<<j)<=n; j++)
for(int i=0; i+(1<<j)-1<n; i++){
int a = dp[i][j-1];
int b = dp[i+(1<<(j-1))][j-1];
dp[i][j] = deep[a] < deep[b] ? a : b;
}
}
int rmq(int x, int y){
int k = (int)(log((y-x+1)*1.0)/log(2.0));
int a = dp[x][k];
int b = dp[y-(1<<k)+1][k];
if(deep[a] < deep[b]) return a;
return b;
}
int lca(int u, int v){
int x = first[u], y = first[v];//首次出现的下标
if(x > y) swap(x,y);
int ans = rmq(x,y);//得到的是下标
return ver[ans];
}
void init(){
cnt = tot = 0;
memset(head, -1, sizeof(head));
memset(vis, false, sizeof(vis));
memset(ok, false, sizeof(ok));
}
//f[]:可以取走的石子个数
//sg[]:0~n的SG函数值
//hash[]:mex{}
int f[N],sg[N],hash[N];
void getSG(int n)
{
int i,j;
memset(sg,0,sizeof(sg));
for(i=1;i<=n;i++)
{
memset(hash,0,sizeof(hash));
for(j=1;f[j]<=i;j++)
hash[sg[i-f[j]]]=1;
for(j=0;j<=n;j++) //求mes{}中未出现的最小的非负整数
{
if(hash[j]==0)
{
sg[i]=j;
break;
}
}
}
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
LL ret=exgcd(b,a%b,y,x);
y=y-a/b*x;
return ret;
}
LL Read()
{
LL i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
int read()
{
int x = 0, ch = getchar(), v = 1;
while(!isdigit(ch)){
if(ch == '-')
v = -1;
ch = getchar();
}
while(isdigit(ch)){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * v;
}
void sc(LL x)
{
if(x>=10)
sc(x/10);
putchar(x%10+48);
}
#include
using namespace std;
const double eps = 1e-8;
struct node{
double x,y;
} p[510];
double tx,ty;
double r,x,y;
double dis(node a,node b) {return sqrt(pow(a.x - b.x,2) + pow(a.y - b.y,2));}
double th(int n){
double T = 10;
double delta = 0.95;
r = 0x3f3f3f3f;
node a;
a.x = tx;a.y = ty;
while(T > eps){
int k = 0;
double d = 0;
for(int i = 0;i < n;i++){
double f = dis(a,p[i]);
if(f > d){
d = f;
k = i;
}
}
a.x += (p[k].x - a.x) / d * T;
a.y += (p[k].y - a.y) / d * T;
r = min(r,d);
T *= delta;
}
x = a.x;y = a.y;
return 0;
}
int main()
{
int n;
while(cin >> n && n){
tx = 0,ty = 0;
for(int i = 0;i < n;i++){
cin >> p[i].x >> p[i].y;
tx += p[i].x;
ty += p[i].y;
}
tx /= n;
ty /= n;
th(n);
printf("%.2lf %.2lf %.2lf\n",x,y,r);
}
return 0;
}
#include
using namespace std;
const double eps = 1e-8;
struct node{
double x,y;
} p[510];
int n;
double ansx,ansy,r;
double getr(double x,double y){
double dx,dy,tr = 0;
for(int i = 0;i < n;i++){
dx = x - p[i].x;
dy = y - p[i].y;
tr = max(sqrt(pow(x - p[i].x,2) + pow(y - p[i].y,2)),tr);
}
return tr;
}
void th(){
double T = 10;
double delta = 0.95;
r = getr(ansx,ansy);
while(T > eps){
double x = ansx + (rand()*2-RAND_MAX)*T;
double y = ansy + (rand()*2-RAND_MAX)*T;
double d = getr(x,y);
double k = d - r;
if(k < eps){
ansx = x;
ansy = y;
r = d;
}
else if(exp(-k/T) > rand()){
ansx = x;
ansy = y;
}
T *= delta;
}
return;
}
int main()
{
while(cin >> n && n){
ansx = 0,ansy = 0;
for(int i = 0;i < n;i++){
cin >> p[i].x >> p[i].y;
ansx += p[i].x;
ansy += p[i].y;
}
ansx /= n;
ansy /= n;
th();
printf("%.2lf %.2lf %.2lf\n",ansx,ansy,r);
}
return 0;
}
以上最小球半径
#include
using namespace std;
const double eps = 1e-15;
struct node{
int x,y,w;
} p[1005];
int n;
double ansx,ansy,anse;
double gete(double x,double y){
double tx,ty,eng = 0;
for(int i = 0;i < n;i++){
tx = x - p[i].x;
ty = y - p[i].y;
eng += sqrt(tx*tx + ty*ty)*p[i].w;
}
return eng;
}
void sa(){
double t = 2500;
double detla = 0.997;
anse = gete(ansx,ansy);
while(t > eps){
double nowx = ansx + (rand()*2-RAND_MAX)*t;
double nowy = ansy + (rand()*2-RAND_MAX)*t;
double nowe = gete(nowx,nowy);
double k = nowe - anse;
if(k < eps){
ansx = nowx;
ansy = nowy;
anse = nowe;
}
else if(exp(-k/t)*RAND_MAX>rand()){
ansx = nowx;
ansy = nowy;
}
t *= detla;
}
}
int main()
{
cin >> n;
ansx = 0;ansy = 0;
for(int i = 0;i < n;i++){
cin >> p[i].x >> p[i].y >> p[i].w;
ansx += p[i].x;
ansy += p[i].y;
}
ansx /= n;ansy /= n;
sa();sa();
printf("%.3lf %.3lf\n",ansx,ansy);
return 0;
}
洛谷 平衡点
dfs遍历找所有拓扑排序
#include
#include
#include
#include
#include
using namespace std;
int in[30],a[30],ans[30];
bool e[30][30],v[30];
void dfs(int now,int n){
if(now == n){
for(int i = 0;i < n;++i) printf("%c",ans[i]+'a');
printf("\n");
return ;
}
for(int i = 1;i <= n;++i){
if(!v[a[i]] && !in[a[i]]){
ans[now] = a[i];
v[a[i]] = 1;
for(int j = 1;j <= n;++j) if(e[a[i]][a[j]]) in[a[j]]--;
dfs(now+1,n);
v[a[i]] = 0;
for(int j = 1;j <= n;++j) if(e[a[i]][a[j]]) in[a[j]]++;
}
}
return ;
}
int main()
{
string S;
char t,tt;
int cnt,len,C = 0;
while(getline(cin,S)){
memset(e,0,sizeof(e));
memset(v,0,sizeof(v));
memset(in,0,sizeof(in));
stringstream ss(S);
cnt = 0;
while(ss >> t) a[++cnt] = t - 'a';
sort(a+1,a+cnt+1);
getline(cin,S);
stringstream sk(S);
while(sk >> t >> tt){
t -= 'a';
tt -= 'a';
in[tt]++;
e[t][tt] = 1;
}
if(C++) printf("\n");
dfs(0,cnt);
}
return 0;
}
拓扑排序判环 && bfs拓扑排序
#include
using namespace std;
int e[105];
vector <int> v[105];
bool bfs(int n){
int cnt = 0;
queue <int> q;
for(int i = 0;i < n;i++){
if(!e[i]) q.push(i);
}
while(!q.empty()){
int k = q.front();q.pop();
cnt++;
int len = v[k].size();
for(int i = 0;i < len;i++){
e[v[k][i]]--;
if(!e[v[k][i]]) q.push(v[k][i]);
}
}
return n-cnt;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m) && n){
for(int i = 0;i < n;i++){
e[i] = 0;
v[i].clear();
}
int x,y;
while(m--){
scanf("%d%d",&x,&y);
v[x].push_back(y);
e[y]++;
}
if(!bfs(n)) printf("YES\n");
else printf("NO\n");
}
return 0;
}
找最长重复串
#include
#include
#include
#include
#include
using namespace std;
typedef unsigned long long ll;
const int maxn = 4e4+5;
const ll seed = 31;
ll hashs[maxn],k[maxn];
char s[maxn];
int main()
{
int m;
while(~scanf("%d",&m) && m){
scanf("%s",s);
int len = strlen(s);
hashs[0] = 0;k[0] = 1;
for(int i = 1;i <= len;i++){
hashs[i] = hashs[i-1]*seed + s[i-1] - 'a';
k[i] = k[i-1]*seed;
}
int l = 0,r = len+1,mid = 0;
while(l <= r){
mid = (l+r) >> 1;
bool f = 0;
map <ll,int> p;
for(int i = 0;i + mid <= len;i++){
ll temp = hashs[i+mid] - hashs[i]*k[mid];
p[temp]++;
if(p[temp] >= m){f = 1;break;}
}
if(f) l = mid+1;
else r = mid-1;
}
if(!r) printf("none\n");
else{
map <ll,int> p;
int idx = -1;
for(int i = 0;i + r <= len;i++){
ll temp = hashs[i+r] - hashs[i]*k[r];
p[temp]++;
if(p[temp] >= m) idx = i;
}
printf("%d %d\n",r,idx);
}
}
return 0;
}
卡特兰数前几项为 : 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, …
公式一:h(n)= h(0)h(n-1)+h(1)h(n-2) + … + h(n-1)h(0) (n>=2)
公式二:h(n)=h(n-1)(4n-2)/(n+1)
公式三:h(n)=C(2n,n)/(n+1) (n=0,1,2,…)
公式四:h(n)=c(2n,n)-c(2n,n-1)(n=0,1,2,…)
Catalan数的典型应用:
1.由n个+1和n个-1组成的排列中,满足前缀和>=0的排列有Catalan(N)种。
2.括号化问题。左括号和右括号各有n个时,合法的括号表达式的个数有Catalan(N)种。
3.有n+1个数连乘,乘法顺序有Catalan(N)种,相当于在式子上加括号。
4.n个数按照特定顺序入栈,出栈顺序随意,可以形成的排列的种类有Catalan(N)种。
5.给定N个节点,能构成Catalan(N)种种形状不同的二叉树。
6.n个非叶节点的满二叉树的形态数为Catalan(N)。
7.对于一个nn的正方形网格,每次只能向右或者向上移动一格,那么从左下角到右上角的不同种类有Catalan(N)种。
8.对于在n位的2进制中,有m个0,其余为1的catalan数为:C(n,m)-C(n,m-1)。
9.对凸n+2边形进行不同的三角形分割(只连接顶点对形成n个三角形)数为Catalan(N)。
10.将有2n个元素的集合中的元素两两分为n个子集,若任意两个子集都不交叉,那么我们称此划分为一个不交叉划分。此时不交叉的划分数是Catalan(N)。
11.n层的阶梯切割为n个矩形的切法数也是Catalan(N)。
12.在一个2n的格子中填入1到2n这些数值使得每个格子内的数值都比其右边和上边的所有数值都小的情况数也是Catalan(N)。
卡特兰数高精度递推打表
#include
#include
#include
#include
using namespace std;
typedef unsigned long long ll;
const ll k = 100000000;
const int maxn = 105;
ll catalan[maxn][12];
void catalan_table(){
memset(catalan,0,sizeof(catalan));
catalan[0][0] = catalan[0][1] = 1;
int i,j;
for(i = 1;i <= 100;i++){
for(j = 1;j <= (int)catalan[i - 1][0];j++){
catalan[i][j] = 2*(2*(i-1) + 1) * catalan[i - 1][j];
}
ll r1 = 0,r2;
for(j = (int)catalan[i - 1][0];j >= 1;j--){
r2 = (r1 * k + catalan[i][j]) % (i + 1);
catalan[i][j] = (r1 * k + catalan[i][j]) / (i + 1);
r1 = r2;
}
for(j = 1;j <= (int)catalan[i - 1][0];j++){
catalan[i][j + 1] += catalan[i][j] / k;
catalan[i][j] %= k;
}
catalan[i][0] = catalan[i][j] > 0 ?catalan[i - 1][0] + 1 :catalan[i - 1][0];
}
}
int main()
{
catalan_table();
int n;
while(~scanf("%d",&n)){
printf("%lld",catalan[n][catalan[n][0]]);
for(int i = catalan[n][0] - 1;i >= 1;i--){
printf("%08lld",catalan[n][i]);
}
printf("\n");
}
return 0;
}
卡特兰数取模打表
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 1e6+5;
ll catalan[maxn];
ll qpow(ll base,ll n){
ll res = 1;
while(n){
if(n & 1) res = res * base % mod;
base = base * base % mod;
n >>= 1;
}
return res;
}
ll inv(ll n) {return qpow(n,mod - 2);}
void catalan_table(){
catalan[0] = catalan[1] = 1;
for(int i = 2;i <= 1000000;i++){
catalan[i] = catalan[i - 1] * (4*i-2) % mod * inv(i+1) % mod;
}
return;
}
int main()
{
catalan_table();
int n,t;
scanf("%d",&t);
for(int g = 1;g <= t;g++){
scanf("%d",&n);
printf("Case #%d:\n%lld\n",g,catalan[n]);
}
return 0;
}
究极加速打表
void catalan_table(){
catalan[0] = catalan[1] = 1;
f[1] = f[0] = 1;
inv[1] = 1;
for(ll i = 2;i <= 1000000;i++) inv[i] = (mod - mod/i) * inv[mod%i] % mod;
for(ll i = 2;i <= 1000000;i++){
catalan[i] = catalan[i - 1] * (4*i-2) % mod * inv[i+1] % mod;
f[i] = f[i - 1] * i % mod;
}
return;
}
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 2005;
ll C[maxn][maxn],sti[maxn][maxn];
sti_table(){
for(int i = 0;i <= 2000;i++){
C[i][i] = C[i][0] = 1;
sti[i][0] = 0;sti[i][i] = 1;
for(int j = 1;j < i;j++){
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
sti[i][j] = (sti[i - 1][j - 1] + sti[i - 1][j]*(i-1) % mod) % mod;
}
}
}
int main()
{
sti_table();
int n,t;
scanf("%d",&t);
while(t--){
int n,f,b;
scanf("%d%d%d",&n,&f,&b);
if(f+b > n+2) printf("0\n");
else printf("%lld\n",(C[f+b-2][f-1] % mod * sti[n-1][f+b-2] % mod) % mod);
}
return 0;
}
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll mod = 20090126;
const int maxn = 105;
ll f[maxn],sti[maxn][maxn];
sti_table(){
f[0] = f[1] = 1;
for(ll i = 2;i <= 100;i++){
f[i] = (f[i - 1]*i) % mod;
sti[i][1] = 1;
sti[i][i] = 1;
for(ll j = 2;j < i;j++){
sti[i][j] = (sti[i - 1][j - 1] + j*sti[i - 1][j]) % mod;
}
}
}
int main()
{
sti_table();
int n,t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
if(n == 1){printf("1\n");continue;}
ll ans = 0;
for(int i = 1;i <= n;i++) ans = (ans + sti[n][i]*f[i] % mod) % mod;
printf("%lld\n",ans);
}
return 0;
}
const int maxn = 21;
ll Bell[maxn];
void init() {
ll T[maxn];
Bell[0] = 1;
Bell[1] = 1;
T[0] = 1;
for(int i = 2; i < maxn; i++) {
T[i - 1] = Bell[i - 1];
for(int j = i - 2; j >= 0; j--)
T[j] = T[j] + T[j + 1];
Bell[i] = T[0];
}
}
容斥模板
#include
using namespace std;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
return a*b/gcd(a,b);
}
int solve()//求1~n与n互质的数的个数
{
int prime[10]= {2,3,5};
int n=30,ans,num;
ans=0,num=3;//num是素数因子或者因子的个数,这里是素数因子
for(int i=1; i< (1<<num) ; i++)//循环次数,也就是集合个数,也就是有2^num-1个集合
{
//i就是表示第i个集合
//2^n可以用1<
int mult=1,cnt=0;
for(int j=0; j<num; j++) //循环prime中的每个元素
{
if(i & (1<<j))//要掌握&运算,与i的二进制的第j位比较,看是否为1,是则选中
{
cnt++;//记录集合中元素的个数
mult = lcm(mult,prime[j]);//是否要lcm看情况
}
}
if(cnt&1)//集合元素个数为奇数就加,否则减
ans += n/mult;
else
ans -= n/mult;
}
return n-ans;
}
int main()
{
cout<<solve();
return 0;
}
队列解决容斥
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
ll cnt,q[100010];
void div(ll n){
cnt = 0;
for(ll i = 2;i <= (ll)sqrt((double)n);i++){
if(n % i == 0) q[cnt++] = i;
while(n % i == 0) n /= i;
}
if(n > 1) q[cnt++] = n;
return;
}
ll cal(ll n){
ll que[10000],t = 0,sum = 0;
que[t++] = -1;
for(int i = 0;i < cnt;i++){
ll k = t;
for(int j = 0;j < k;j++) que[t++] = que[j]*(-q[i]);
}
for(int i = 1;i < t;i++) sum += n / que[i];
return sum;
}
int main()
{
ll t;
scanf("%lld",&t);
while(t--){
ll a,b,ans = 0;
scanf("%lld%lld",&a,&b);
for(ll i = 1;i <= a;i++){
div(i);
ans += b - cal(b);
}
printf("%lld\n",ans);
}
return 0;
}
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int main()
{
int n;
while(cin >> n){
int vis[10005],a[10005];
memset(vis,0,sizeof(vis));
for(int i = 1;i <= n;i++) cin >> a[i];
int sum = 0;
for(int i = 1;i <= n;i++){
sum += a[i];
sum %= n;
if(sum == 0){
cout << i << endl;
for(int j = 1;j <= i;j++) cout << a[j] << endl;
break;
}
if(vis[sum]){
cout << i - vis[sum] << endl;
for(int j = vis[sum] + 1;j <= i;j++) cout << a[j] << endl;
break;
}
vis[sum] = i;
}
}
return 0;
}
枚举三个点点
先枚举前面的数(i),再枚举后面的数(j),最后枚举中间的分割线(c)(三重循环) 用i到c-1之间的数加上c到j的数再加上之前的得分,再将所有的值去最小。
f[i][j]=min(f[i][j],f[i][c−1]+f[c][j]+s[j]−s[i−1])
#include
#include
#include
#include
using namespace std;
int n,s[101],f[101][101],x;
int main()
{
memset(f,127/3,sizeof(f));//因为要去最小的,所以要先赋一个大的值
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
s[i]=s[i-1]+x;//前缀和
f[i][i]=0;//清零单个的
}
for (int i=n-1;i>=1;i--)
for (int j=i+1;j<=n;j++)
for (int c=i+1;c<=j;c++)
f[i][j]=min(f[i][j],f[i][c-1]+f[c][j]+s[j]-s[i-1]);
printf("%d",f[1][n]);
}
先枚举长度
先枚举长度(k),再枚举前面的数(i),最后枚举分割线(c),然后后面的数(j)就可以求出来了:
j=i+k−1
f[i][j]=min(f[i][j],f[i][c−1]+f[c][j]+s[j]−s[i−1])
#include
#include
#include
#include
using namespace std;
int n,s[101],f[101][101],x;
int main()
{
memset(f,127/3,sizeof(f));//因为要去最小的,所以要先赋一个大的值
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
s[i]=s[i-1]+x;//前缀和
f[i][i]=0;//清零单个的
}
for (int i=n-1;i>=1;i--)
for (int j=i+1;j<=n;j++)
for (int c=i+1;c<=j;c++)
f[i][j]=min(f[i][j],f[i][c-1]+f[c][j]+s[j]-s[i-1]);
printf("%d",f[1][n]);
}
先枚举长度,后枚举点
方法与前面的一样,但是f[i][j]表示的是从第i个开始后面的j个数
f[i][j]=min(f[i][j],f[i][c]+f[i+c][j−c]+s[i+j−1]−s[i−1])
#include
#include
#include
#include
using namespace std;
int n,x,s[101],f[101][101];
int main()
{
memset(f,127/3,sizeof(f));
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
s[i]=s[i-1]+x;//前缀和
f[i][1]=0;
}
for (int j=2;j<=n;j++)//枚举长度,但表达不一样
for (int i=1;i<=n-j+1;i++)//枚举前面的数
for (int c=1;c<j;c++)//枚举分割线
f[i][j]=min(f[i][j],f[i][c]+f[i+c][j-c]+s[i+j-1]-s[i-1]);
printf("%d",f[1][n]);
}
求最小环
int val[maxn + 1][maxn + 1]; // 原图的邻接矩阵
inline int floyd(const int &n) {
static int dis[maxn + 1][maxn + 1]; // 最短路矩阵
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) dis[i][j] = val[i][j]; // 初始化最短路矩阵
int ans = inf;
for (int k = 1; k <= n; ++k) {
for (int i = 1; i < k; ++i)
for (int j = 1; j < i; ++j)
ans = std::min(ans, dis[i][j] + val[i][k] + val[k][j]); // 更新答案
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
dis[i][j] = std::min(
dis[i][j], dis[i][k] + dis[k][j]); // 正常的 floyd 更新最短路矩阵
}
return ans;
}
单调队列优化1
#include
#include
#include
#include
#include
using namespace std;
typedef pair<int, int>pii;
struct cmp
{
bool operator()(pii a, pii b) { return a.first > b.first; }
};
int n, m, s;
int to[200005], head[200005], nx[200005], va[200005],cnt,dis[100005];
void add(int u, int v, int w)
{
to[++cnt] = v;
va[cnt] = w;
nx[cnt] = head[u];
head[u] = cnt;
}
void dijstra(int s)
{
for (int i = 1; i <= n; i++)dis[i] = 1e9+7;
dis[s] = 0;
priority_queue<pii, vector<pii>, cmp>q;
q.push(make_pair(0, s));
while (!q.empty())
{
pii u = q.top();
q.pop();
if (u.first > dis[u.second])continue;
for (int i = head[u.second]; i; i = nx[i])
{
int j = to[i];
if (dis[j] > u.first + va[i])
{
dis[j] = u.first + va[i];
q.push(make_pair(dis[j], j));
}
}
}
}
int main()
{
while (cin >> n >> m >> s)
{
cnt = 0;
while (m--)
{
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
}
dijstra(s);
for (int i = 1; i <= n; i++)printf("%d ", dis[i]);
}
}
单调队列优化2
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define INF 0x3f3f3f3f
#defien maxn 1005
using namespace std;
//重点!!!!!
//写整个程序时一定要maps[i][j] = maps[j][i] 初始化!!!!!
int dis[maxn],maps[maxn][maxn],n,m;
struct Node{
int n,v; //存放结点编号和到初始点的距离
}node;
bool operator < (Node a,Node b){
a.v == b.v ? a.n>b.n : a.v>b.v; //先出小
}
priority_queue<Node> Q; //优先从小到大
void Init(){
for(int i = 1;i <= n;i++){
dis[i] = INF;
for(int j = 1;j <= i; j++)
i == j ? maps[i][j] = 0 : maps[i][j] = maps[j][i] = INF;
}
}
void Dijkstra(int s){
while(!Q.empty()) Q.pop(); //清空
dis[s] = 0;
Node P;
P.n = s;
P.v = 0;
Q.push(P); //将起点放入队列
while(!Q.empty()){
for(int i = 2; i <= n; i++){
if(dis[i] > dis[Q.top().n] + maps[Q.top().n][i]){
dis[i] = dis[Q.top().n] + maps[Q.top().n][i];
P.n = i;
P.v = dis[i];
Q.push(P);
}
}
}
Q.pop();
}
#include
#include
#include
#include
#include
#include
#define N 10009
#define M 500009
using namespace std;
int dis[N],nxt[M],to[M],w[M],head[N];
int n,m,s;
queue<int> q;
inline int read(){
char ch;
int f=1,res=0;
while((ch=getchar())<'0'||ch>'9')
if(ch=='-') f=-1;
while(ch>='0'&&ch<='9')
{
res=(res<<1)+(res<<3)+(ch^48);//注意位运算的优先级别
ch=getchar();
}
return f*res;
}
int tot=0;
bool vis[N];
void add(int x,int y,int z){
nxt[++tot]=head[x];
head[x]=tot;
w[tot]=z;
to[tot]=y;
}
void spfa(int u){
q.push(u);
vis[u]=1;
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=0;
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
if(dis[x]+w[i]<dis[y]){
dis[y]=dis[x]+w[i];
if(!vis[y]) vis[y]=1,q.push(y);
}
}
}
}
int main(){
n=read();m=read();s=read();
int i,j,k;
for(i=1;i<=m;++i)
{
int x,y,z;
x=read();y=read();z=read();
add(x,y,z);
//add(read(),read(),read());不能直接这样写
}
for(i=1;i<=n;++i)
{
dis[i]=2147483647;//这是模板题要求的大小,一般情况下设为极大值即可
vis[i]=0;
}
dis[s]=0;
spfa(s);
for(i=1;i<=n;++i)
printf("%d ",dis[i]);
return 0;
}
dfs判负环
#include
#include
#include
#include
#include
#include
#include
#define inf 0x3f3f3f3f
using namespace std;
const int inf=0x3f3f3f3f;
const int maxm=111110;
const int maxn=20020;
int head[maxn],tot,n,m;
struct edge{
int to;
int w;
int nxt;
}e[maxm];
void add_edge(int u,int v,int w){
e[tot].w=w;
e[tot].to=v;
e[tot].nxt=head[u];
head[u]=tot++;
}
bool vis[maxn];
int d[maxn];
//
bool dfs_spfa(int u){
vis[u]=1;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].to;
if(d[v]>d[u]+e[i].w){
d[v]=d[u]+e[i].w;
if(vis[v]||dfs_spfa(v)) return 1;
}
}
vis[u]=0;
return 0;
}
int main(){
int a,b,c,T;
scanf("%d",&T);
while(T--){
tot=0;
memset(head,-1,sizeof head);
memset(vis,0,sizeof vis);
memset(d,0,sizeof d);
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){//注意为双向边
scanf("%d%d%d",&a,&b,&c);
if(c<0) add_edge(a,b,c);
else add_edge(a,b,c),add_edge(b,a,c);
}
bool flag=false;
for(int i=1;i<=n;i++){
if(dfs_spfa(i)){
flag=true;
break;
}
}
if(flag) puts("YE5");
else puts("N0");
}
return 0;
}
bfs1
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int inf=0x3f3f3f3f;
const int maxm=11111;
const int maxn=5000;
int head[maxn],tot,n,m,cnt[maxn],w;
struct edge{
int to;
int w;
int nxt;
}e[maxm];
void add_edge(int u,int v,int w){
e[tot].w=w;
e[tot].to=v;
e[tot].nxt=head[u];
head[u]=tot++;
}
bool vis[maxn];
queue<int>que;//队列是点的队列
int d[maxn];
bool spfa(int s){
memset(cnt,0,sizeof cnt);
fill(d+1,d+n+1,inf);
memset(vis,0,sizeof vis);
while(!que.empty()) que.pop();
que.push(s);
vis[s]=true;
d[s]=0;
while (!que.empty()){
int u=que.front();
que.pop();
vis[u]=false;
for (int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].to;
int w=e[i].w;
if (d[v]>d[u]+w){
d[v]=d[u]+w;
cnt[v]=cnt[u]+1;
if(cnt[v]>n) return true;
if (!vis[v]){
vis[v]=true;
que.push(v);
}
}
}
}
return false;
}
int main(){
int a,b,c,T;
scanf("%d",&T);
while(T--){
tot=0;
memset(head,-1,sizeof head);
memset(vis,0,sizeof vis);
memset(d,0,sizeof d);
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){//注意为双向边
scanf("%d%d%d",&a,&b,&c);
add_edge(a,b,c);
if(c>=0)add_edge(b,a,c);
}
if(spfa(1)) puts("YE5");
else puts("N0");