模板记录——赛前准备

0.STL操作

vector 动态数组

头文件

#include <vector>

定义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值的下标位置

String 字符串类型

头文件

#include <string>

定义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

stack 栈(先进后出)

头文件

#include <stack>

定义stack

stack <int> a;

访问方式

对于stack而言,中间的元素是无法访问的,只能通过a.top()访问栈顶元素
a.top();

常规操作(一些自带的函数)

基础函数
a.push(x);  # 将x放到栈顶
a.pop();  # 删除栈顶元素
a.size();  # 返回栈内元素个数
a.empty();  # 返回栈是否为空

由于栈的使用对空间占用较大(深度大了不能及时释放),所以一下两点可以解决
1.在程序中调用大系统的栈,依赖于系统和编译器(不推荐)
2.手工写栈(没试过,但感觉数组可以实现,自己封装一下应该就ok)

queue 队列(先进先出,公平原则)

头文件

#include <queue>

定义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的结合中有相关赘述(只是板块分的不太好),此处就不再说了。

set 集合(去重性,有自动排序的功能)

头文件

#include <set>

定义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(取集合对称差集)等函数。

map 映射(映射,去重性,有自动排序的功能)

头文件

#include <map>

定义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存在,但不知道怎么用,暂不整理

Struct 结构体与stl的结合(排序)

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的 < 运算符重载必须得写成友元。

1.素数

线性筛(欧拉筛)
取出素数版本外加判断表

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 <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
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 <bits/stdc++.h>
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<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
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 <cstdio>
#include <cstring>
#include <cstdlib>
#include <time.h>
#include <iostream>
#include <algorithm>
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;

2.并查集

裸版查找

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];
}

(2)种类并查集

对立面并查集

#include <iostream>
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 <iostream>
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 <iostream>
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 即多倍数组

(3)区间并查集

** 本质思想,维护根节点权值为0,边权转点权差**

#include <iostream>
#include <cstring>
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

(4)带权并查集

结点权值和集合权值

#include <iostream>
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 <iostream>
#include <set>
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;
}

百度之星(普通权值变非冲突合集(权值))

3.RMQ

基础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 <cstdio>
#include <cmath>
#include <algorithm>
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 <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <algorithm>
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 <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <algorithm>
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]);
}

4.欧拉函数

(1)求欧拉函数

直接求解欧拉函数

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<cstdio>
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);
    }

(2)多次幂下的扩展欧拉降幂

细节mod(解决mod选择问题)

ll Mod(ll x, ll mod){
    return x < mod ? x : x % mod + mod;
}

super_log

#include<bits/stdc++.h>
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<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#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;
}

5.最长上升子序列(最长下降)

nlogn

#include <bits/stdc++.h>
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 <iostream>
#include <cstring>
#include <string>
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

6.逆元

拓展欧几里得

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;
}

7.割边、割点

割点


#include <algorithm>
#include <cstring>
#include <cstdio>
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 <algorithm>
#include <cstring>
#include <cstdio>
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 <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
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 <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstring>
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;
}

8.双联通分量

点双(染点)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<set>
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 <bits/stdc++.h>
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 <iostream>
#include <vector>
#include <algorithm>
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 <bits/stdc++.h>
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 <bits/stdc++.h>
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<bits/stdc++.h>
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<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
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 <iostream>
#include <vector>
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<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
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;
}

9.LCA

基础倍增法

#include <bits/stdc++.h>
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<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#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 <bits/stdc++.h>
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));
}

10.SG

//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;
            }
        }
    }
}

11.扩展欧几里得


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;
}

12.快读/快写


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);
}

13.退火

#include <bits/stdc++.h>
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 <bits/stdc++.h>
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 <bits/stdc++.h>
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;
}

洛谷 平衡点

14.拓扑排序

dfs遍历找所有拓扑排序

#include <iostream>
#include <stack>
#include <cstring>
#include <sstream>
#include <algorithm>
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 <bits/stdc++.h>
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;
}

15.字符串hash

找最长重复串

#include <iostream>
#include <cstring>
#include <string>
#include <map>
#include <cstdio>
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;
}

16.卡特兰数

卡特兰数前几项为 : 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)
(4
n-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.在一个2
n的格子中填入1到2n这些数值使得每个格子内的数值都比其右边和上边的所有数值都小的情况数也是Catalan(N)。

卡特兰数高精度递推打表

#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
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 <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
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;
}

17.斯特林数

在这里插入图片描述
在这里插入图片描述
第一类斯特林数打表

#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
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 <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
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];
	}
}

18.容斥

容斥模板


#include <bits/stdc++.h>
 
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<<n为运算表示
        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 <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <queue>
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;
}

19.鸽笼原理

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
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;
}

20.区间dp

枚举三个点点
先枚举前面的数(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<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
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<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
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<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
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]);
}

21.floyd

求最小环


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;
}

22.dijkstra

单调队列优化1

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
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<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#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();
}

23.SPFA


#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#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<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<queue>
#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<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<queue>
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");
    }
    return 0;
}

bfs2(num > n)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxm=11111;
const int maxn=5000;
int head[maxn],tot,n,m,num[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(num,0,sizeof num);
    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;
                if (!vis[v]){
                    vis[v]=true;
                    que.push(v);//hash一下,可判断是否存在负环
                    num[v]++;
                    if(num[v]>n) return true;
                }
            }
        }
    }
    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");
    }
    return 0;
}

24.Dinic模板

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN = 10000 + 5;
const int MAXM = 100000 + 5;
const int INF = 1e9;
int n,m;
int s,t;//源点 汇点
int maxflow;//答案
struct Edge {
    int next;
    int to,flow;
} l[MAXM << 1];
int head[MAXN],cnt = 1;
int deep[MAXN],cur[MAXN];//deep记录bfs分层图每个点到源点的距离
queue <int> q;
inline void add(int x,int y,int z) {
    cnt++;
    l[cnt].next = head[x];
    l[cnt].to = y;
    l[cnt].flow = z;
    head[x] = cnt;
    return;
}
int min(int x,int y) {
    return x < y ? x : y;
}
int dfs(int now,int t,int lim) {//分别是当前点,汇点,当前边上最小的流量
    if(!lim || now == t) return lim;//终止条件
//  cout<<"DEBUG: DFS HAS BEEN RUN!"<<endl;
    int flow = 0;
    int f;
    for(int i = cur[now]; i; i = l[i].next) {//注意!当前弧优化
        cur[now] = i;//记录一下榨取到哪里了
        if(deep[l[i].to] == deep[now] + 1 //谁叫你是分层图
            && (f = dfs(l[i].to,t,min(lim,l[i].flow)))) {//如果还能找到增广路
        flow += f;
            lim -= f;
            l[i].flow -= f;
            l[i ^ 1].flow += f;//记得处理反向边
            if(!lim) break;//没有残量就意味着不存在增广路
        }
    }
    return flow;
}
bool bfs(int s,int t) {
    for(int i = 1; i <= n; i++) {
        cur[i] = head[i];//拷贝一份head,毕竟我们还要用head
        deep[i] = 0x7f7f7f7f;
    }
    while(!q.empty()) q.pop();//清空队列 其实没有必要了
    deep[s] = 0;
    q.push(s);
    while(!q.empty()) {
        int tmp = q.front();
        q.pop();
        for(int i = head[tmp]; i; i = l[i].next) {
            if(deep[l[i].to] > INF && l[i].flow) {//有流量就增广
            //deep我赋的初值是0x7f7f7f7f 大于 INF = 1e9)
                deep[l[i].to] = deep[tmp] + 1;
                q.push(l[i].to);
            }
        }
    }
    if(deep[t] < INF) return true;
    else return false;
}
void dinic(int s,int t) {
    while(bfs(s,t)) {
        maxflow += dfs(s,t,INF);
//      cout<<"DEBUG: BFS HAS BEEN RUN!"<<endl;
    }
}
int main() {
    cin>>n>>m;//点数边数
    cin>>s>>t;
    int x,y,z;
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,0);
    }
//  cout<<"DEBUG: ADD FININSHED!"<<endl;
    dinic(s,t);
    printf("%d",maxflow);
    return 0;
}

25.最小生成树

kal

const int maxn=107;
struct note
{
    int a, b, c;
    bool friend operator < (note a, note b)
    {
        return a.c < b.c;
    }
}mp[maxn*maxn];
struct tree
{
    int pre, rak;
}tre[maxn];

int find(int x)
{
    if(tre[x].pre==-1) return x;
    else return tre[x].pre=find(tre[x].pre);
}

void unit(int x, int y)
{
    if(tre[x].rak > tre[y].rak)
    {
        tre[y].pre=x;
    }
    else {
        tre[x].pre=y;
        if(tre[x].rak == tre[y].rak)
            tre[y].rak++;
    }
}
int n, m;
void init()
{
    for(int i=1; i<=m; i++)
    {
        tre[i].pre=-1;
        tre[i].rak=0;
    }
}
int main()
{
    while(scanf("%d%d", &n, &m) && n!=0)
    {
        init();
        for(int i=0; i<n; i++)
        {
            scanf("%d%d%d", &mp[i].a, &mp[i].b, &mp[i].c);
        }
        sort(mp, mp+n);
        int x, y, ans=0, cnt=0;
        for(int i=0; i<n; i++)
        {
            x=find(mp[i].a);
            y=find(mp[i].b);
            if(x!=y)
            {
                unit(x, y);
                ans+=mp[i].c;
                cnt++;
            }
            if(cnt==m-1) break;
        }
        if(cnt==m-1)
            printf("%d\n", ans);
        else printf("?\n");
    }
    return 0;
}

pri

const int inf=0x3f3f3f3f;
const int maxn=1e3;
struct Node{
    int x, y;
}node[maxn];
int mp[maxn][maxn];
int vis[maxn];
int dis[maxn];//这里的dis存储的是其他各个点,到最小生成树中任意一点的最小值。
int line[maxn];
int n, m;
void init()
{
    for(int i=1; i<=n; i++)
    {
        dis[i]=mp[1][i];//开始的时候任选1号顶点加入到生成树中。
        line[i]=1; //默认没有加入到生成树的点到生成树中任意一点的最小距离的点是1;
        vis[i]=0; //默认没有点加入到生成树中。
    }
}
bool prim()
{
    vis[1]=1;
    for(int i=1; i<n; i++)
    {
        int tmp=inf, k;
        for(int j=1; j<=n; j++)
        {
            if(!vis[j] && dis[j]<tmp)
            {
                tmp=dis[j];
                k=j;
            }
        }
        if(tmp==inf) return false;//生成最小树失败,该图不是连通的。
        vis[k]=1;
        if(mp[k][line[k]]!=0)
        {
            printf("%d %d\n", k, line[k]);
        }
        for(int j=1; j<=n; j++) //以新加入生成树的点作为中间点,看看能优化
        {
            if(!vis[j] && dis[j] > mp[j][k])
            {
                line[j]=k;
                dis[j]=mp[j][k];
            }
        }
    }
    return true;
}

26.树的直径

dfs

#include<iostream>
#include<stdio.h>
#include<map>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
int n,m,maxid,maxn;
vector<int> G[10009];
int f[10009];int d[10009];
int vis[10009];int path[10009];
int book[10009];int du[10009];

void dfs(int x){
	for(int i=0;i<G[x].size();i++){
		int u=G[x][i];
		if(vis[u]==1) continue;
		vis[u]=1;
		if(d[u]<d[x]+1){

			d[u]=d[x]+1;
			if(maxn<d[u]){
				maxid=u;
				maxn=d[u];				
			}
		}
		path[u]=x;
		dfs(u);
	}
	return;
}

int find(int x){
	return x==f[x]?x:f[x]=find(f[x]);
}

int main(){
	int cnt=1;
	while(scanf("%d",&n)){
		int flag=0;
		if(n==0) break;		
		scanf("%d",&m);
		for(int i=1;i<=n;i++) G[i].clear();
		memset(du,0,sizeof(du));
		memset(book,0,sizeof(book));
		for(int i=1;i<=n;i++) f[i]=i;
		for(int i=1;i<=m;i++){
			int ta,tb;
			scanf("%d%d",&ta,&tb);
			G[ta].push_back(tb);
			G[tb].push_back(ta);
			du[ta]++;
			du[tb]++;
			int fx=find(ta);
			int fy=find(tb);
			if(fx==fy)
				flag=1;
			else f[fx]=fy;
		}
		if(flag==1){
			printf("Graph %d is not a caterpillar.\n",cnt++);
			continue;			
		}//并查集判环

		int sum=0;
		for(int i=1;i<=n;i++){
			if(f[i]==i) sum++;
		}
		if(sum>1){
			printf("Graph %d is not a caterpillar.\n",cnt++);
			continue;
		}//并查集判断连通性
		for(int i=1;i<=n;i++) f[i]=i;
		maxn=0;
		maxid=0;
		memset(vis,0,sizeof(vis));
		dfs(1);//dfs寻找端点
		int tem=maxid;
		memset(vis,0,sizeof(vis));
		memset(path,-1,sizeof(path));
		memset(d,0,sizeof(d));
		maxid=0;
		maxn=0;
		path[tem]=-1;
		vis[tem]=1;
		dfs(tem);//dfs寻找树的直径
		for(int i=maxid;i!=-1;i=path[i]){
			book[i]=1;
		}	//标记树的直径	
		int ct=0;
		for(int i=1;i<=n;i++){
			if(book[i]==0&&du[i]>=2) ct++;//判断是否符合要求
		}
		if(ct>=1) printf("Graph %d is not a caterpillar.\n",cnt++);
		else printf("Graph %d is a caterpillar.\n",cnt++);
	}
} 

bfs

#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<map>
#include<string.h>
#include<queue>
using namespace std;
#define M 2000005
int head[M*2],ver[M],edge[M],next[M*2];
long long ans[M];int v[M]; int tot;
long long maxn;


void add(int x,int y,int z){
//	cout<<"*"<<endl;
	ver[++tot]=y;edge[tot]=z;
	next[tot]=head[x],head[x]=tot;
//	printf("x:%d  tot:%d\n",x,tot);
}

int bfs(int x){
	queue<int> q;
	while(q.size()) q.pop();
	memset(ans,0,sizeof(ans));
	memset(v,0,sizeof(v));
	q.push(x);
	v[x]=1;
	int maxid=0;
	while(!q.empty()){
		int now=q.front();
		q.pop();
		for(int i=head[now];i;i=next[i]){
			int y=ver[i];
			if(v[y]==1) continue;
			v[y]=1;
			ans[y]=ans[now]+edge[i];	
			if(ans[y]>maxn){
				maxn=ans[y];
				maxid=y;
			}
			q.push(y);
		}
	}
	return maxid;
}


int main(){
	tot=0;
	int ta,tb,tc;
	while(~scanf("%d%d%d",&ta,&tb,&tc)){
		add(ta,tb,tc);
		add(tb,ta,tc);
	}
	int p=bfs(1);
	int ed=bfs(p);
	printf("%lld\n",maxn);
}

27.强联通分量

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<cstdlib>
#include<algorithm>
 
using namespace std;
 
#define LL long long
#define ULL unsigned long long
#define UINT unsigned int
#define MAX_INT 0x7fffffff
#define MAX_LL 0x7fffffffffffffff
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
 
#define MAXN 11111
#define MAXM 100011
 
struct edge{
    int u, v, nxt;
}e[MAXM];
int h[MAXN],dfn[MAXN],low[MAXN],cnt,id[MAXN];
int n,m,cc;
int vis[MAXN],ins[MAXN],s[MAXN],top;
int tsp;
 
inline void add(int u, int v){
    e[cc]=(edge){u, v, h[u]};
    h[u]=cc++;
}
 
void tarjan(int u){
    int v,i;
    s[top++]=u;
    ins[u]=vis[u]=1;
    low[u]=dfn[u]=++tsp;
 
    for(i=h[u]; i!=-1; i=e[i].nxt){
        v=e[i].v;
        if(!vis[v]) tarjan(v),low[u]=MIN(low[u], low[v]);
        else if(ins[v]) low[u]=MIN(low[u], dfn[v]);
    }
 
    if(low[u]==dfn[u]){
        ++cnt;
        do{
            v=s[--top];
            ins[v]=0;
            id[v]=cnt;
        }while(u!=v);
    }
}
 
int main(){
    while(scanf(" %d %d",&n,&m)==2 && (n||m)){
        int i,u,v,j;
        memset(h, -1, sizeof(h));       cc=0;
        for(i=0; i<m; i++){
            scanf(" %d %d",&u,&v);
            add(u, v);
        }
        memset(vis, 0, sizeof(vis));
        memset(ins, 0, sizeof(ins));
        top=tsp=cnt=0;
        for(i=1; i<=n; i++)
            if(!vis[i]) tarjan(i);
        for(j=id[1],i=2; i<=n; i++){
            if(j!=id[i]) break;
           // printf("i:%d %d\n",i,id[i]);
        }
        if(i<=n) cout<<"No"<<endl;
        else cout<<"Yes"<<endl;
    }
    return 0;
}

28.高精度

两个高精度非负整数相加

string add(string a,string b)//只限两个非负整数相加
{
    const int L=1e5;
    string ans;
    int na[L]={0},nb[L]={0};
    int la=a.size(),lb=b.size();
    for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
    for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
    int lmax=la>lb?la:lb;
    for(int i=0;i<lmax;i++) na[i]+=nb[i],na[i+1]+=na[i]/10,na[i]%=10;
    if(na[lmax]) lmax++;
    for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
    return ans;
}

非负相剪

string sub(string a,string b)//只限大的非负整数减小的非负整数
{
    const int L=1e5;
    string ans;
    int na[L]={0},nb[L]={0};
    int la=a.size(),lb=b.size();
    for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
    for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
    int lmax=la>lb?la:lb;
    for(int i=0;i<lmax;i++)
    {
        na[i]-=nb[i];
        if(na[i]<0) na[i]+=10,na[i+1]--;
    }
    while(!na[--lmax]&&lmax>0)  ;lmax++;
    for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
    return ans;
}

朴素乘法

string mul(string a,string b)//高精度乘法a,b,均为非负整数
{
    const int L=1e5;
    string s;
    int na[L],nb[L],nc[L],La=a.size(),Lb=b.size();//na存储被乘数,nb存储乘数,nc存储积
    fill(na,na+L,0);fill(nb,nb+L,0);fill(nc,nc+L,0);//将na,nb,nc都置为0
    for(int i=La-1;i>=0;i--) na[La-i]=a[i]-'0';//将字符串表示的大整形数转成i整形数组表示的大整形数
    for(int i=Lb-1;i>=0;i--) nb[Lb-i]=b[i]-'0';
    for(int i=1;i<=La;i++)
        for(int j=1;j<=Lb;j++)
        nc[i+j-1]+=na[i]*nb[j];//a的第i位乘以b的第j位为积的第i+j-1位(先不考虑进位)
    for(int i=1;i<=La+Lb;i++)
        nc[i+1]+=nc[i]/10,nc[i]%=10;//统一处理进位
    if(nc[La+Lb]) s+=nc[La+Lb]+'0';//判断第i+j位上的数字是不是0
    for(int i=La+Lb-1;i>=1;i--)
        s+=nc[i]+'0';//将整形数组转成字符串
    return s;
}

FFT优化乘法

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
#define L(x) (1 << (x))
const double PI = acos(-1.0);
const int Maxn = 133015;
double ax[Maxn], ay[Maxn], bx[Maxn], by[Maxn];
char sa[Maxn/2],sb[Maxn/2];
int sum[Maxn];
int x1[Maxn],x2[Maxn];
int revv(int x, int bits)
{
    int ret = 0;
    for (int i = 0; i < bits; i++)
    {
        ret <<= 1;
        ret |= x & 1;
        x >>= 1;
    }
    return ret;
}
void fft(double * a, double * b, int n, bool rev)
{
    int bits = 0;
    while (1 << bits < n) ++bits;
    for (int i = 0; i < n; i++)
    {
        int j = revv(i, bits);
        if (i < j)
            swap(a[i], a[j]), swap(b[i], b[j]);
    }
    for (int len = 2; len <= n; len <<= 1)
    {
        int half = len >> 1;
        double wmx = cos(2 * PI / len), wmy = sin(2 * PI / len);
        if (rev) wmy = -wmy;
        for (int i = 0; i < n; i += len)
        {
            double wx = 1, wy = 0;
            for (int j = 0; j < half; j++)
            {
                double cx = a[i + j], cy = b[i + j];
                double dx = a[i + j + half], dy = b[i + j + half];
                double ex = dx * wx - dy * wy, ey = dx * wy + dy * wx;
                a[i + j] = cx + ex, b[i + j] = cy + ey;
                a[i + j + half] = cx - ex, b[i + j + half] = cy - ey;
                double wnx = wx * wmx - wy * wmy, wny = wx * wmy + wy * wmx;
                wx = wnx, wy = wny;
            }
        }
    }
    if (rev)
    {
        for (int i = 0; i < n; i++)
            a[i] /= n, b[i] /= n;
    }
}
int solve(int a[],int na,int b[],int nb,int ans[])
{
    int len = max(na, nb), ln;
    for(ln=0; L(ln)<len; ++ln);
    len=L(++ln);
    for (int i = 0; i < len ; ++i)
    {
        if (i >= na) ax[i] = 0, ay[i] =0;
        else ax[i] = a[i], ay[i] = 0;
    }
    fft(ax, ay, len, 0);
    for (int i = 0; i < len; ++i)
    {
        if (i >= nb) bx[i] = 0, by[i] = 0;
        else bx[i] = b[i], by[i] = 0;
    }
    fft(bx, by, len, 0);
    for (int i = 0; i < len; ++i)
    {
        double cx = ax[i] * bx[i] - ay[i] * by[i];
        double cy = ax[i] * by[i] + ay[i] * bx[i];
        ax[i] = cx, ay[i] = cy;
    }
    fft(ax, ay, len, 1);
    for (int i = 0; i < len; ++i)
        ans[i] = (int)(ax[i] + 0.5);
    return len;
}
string mul(string sa,string sb)
{
    int l1,l2,l;
    int i;
    string ans;
    memset(sum, 0, sizeof(sum));
    l1 = sa.size();
    l2 = sb.size();
    for(i = 0; i < l1; i++)
        x1[i] = sa[l1 - i - 1]-'0';
    for(i = 0; i < l2; i++)
        x2[i] = sb[l2-i-1]-'0';
    l = solve(x1, l1, x2, l2, sum);
    for(i = 0; i<l || sum[i] >= 10; i++) // 进位
    {
        sum[i + 1] += sum[i] / 10;
        sum[i] %= 10;
    }
    l = i;
    while(sum[l] <= 0 && l>0)    l--; // 检索最高位
    for(i = l; i >= 0; i--)    ans+=sum[i] + '0'; // 倒序输出
    return ans;
}
int main()
{
    cin.sync_with_stdio(false);
    string a,b;
    while(cin>>a>>b) cout<<mul(a,b)<<endl;
    return 0;
}

高精度乘蛋精度1

string mul(string a,int b)//高精度a乘单精度b
{
    const int L=100005;
    int na[L];
    string ans;
    int La=a.size();
    fill(na,na+L,0);
    for(int i=La-1;i>=0;i--) na[La-i-1]=a[i]-'0';
    int w=0;
    for(int i=0;i<La;i++) na[i]=na[i]*b+w,w=na[i]/10,na[i]=na[i]%10;
    while(w) na[La++]=w%10,w/=10;
    La--;
    while(La>=0) ans+=na[La--]+'0';
    return ans;
}

高精度除法

int sub(int *a,int *b,int La,int Lb)
{
    if(La<Lb) return -1;//如果a小于b,则返回-1
    if(La==Lb)
    {
        for(int i=La-1;i>=0;i--)
            if(a[i]>b[i]) break;
            else if(a[i]<b[i]) return -1;//如果a小于b,则返回-1
 
    }
    for(int i=0;i<La;i++)//高精度减法
    {
        a[i]-=b[i];
        if(a[i]<0) a[i]+=10,a[i+1]--;
    }
    for(int i=La-1;i>=0;i--)
        if(a[i]) return i+1;//返回差的位数
    return 0;//返回差的位数
 
}
string div(string n1,string n2,int nn)
//n1,n2是字符串表示的被除数,除数,nn是选择返回商还是余数
{
    const int L=1e5;
    string s,v;//s存商,v存余数
     int a[L],b[L],r[L],La=n1.size(),Lb=n2.size(),i,tp=La;
     //a,b是整形数组表示被除数,除数,tp保存被除数的长度
     fill(a,a+L,0);fill(b,b+L,0);fill(r,r+L,0);//数组元素都置为0
     for(i=La-1;i>=0;i--) a[La-1-i]=n1[i]-'0';
     for(i=Lb-1;i>=0;i--) b[Lb-1-i]=n2[i]-'0';
     if(La<Lb || (La==Lb && n1<n2)) {
            //cout<<0<<endl;
     return n1;}//如果a<b,则商为0,余数为被除数
     int t=La-Lb;//除被数和除数的位数之差
     for(int i=La-1;i>=0;i--)//将除数扩大10^t倍
        if(i>=t) b[i]=b[i-t];
        else b[i]=0;
     Lb=La;
     for(int j=0;j<=t;j++)
     {
         int temp;
         while((temp=sub(a,b+j,La,Lb-j))>=0)//如果被除数比除数大继续减
         {
             La=temp;
             r[t-j]++;
         }
     }
     for(i=0;i<L-10;i++) r[i+1]+=r[i]/10,r[i]%=10;//统一处理进位
     while(!r[i]) i--;//将整形数组表示的商转化成字符串表示的
     while(i>=0) s+=r[i--]+'0';
     //cout<<s<<endl;
     i=tp;
     while(!a[i]) i--;//将整形数组表示的余数转化成字符串表示的</span>
     while(i>=0) v+=a[i--]+'0';
     if(v.empty()) v="0";
     //cout<<v<<endl;
     if(nn==1) return s;//返回商 
     if(nn==2) return v;//返回余数 
}

高精度除以单精度

string div(string a,int b)//高精度a除以单精度b
{
    string r,ans;
    int d=0;
    if(a=="0") return a;//特判
    for(int i=0;i<a.size();i++)
    {
            r+=(d*10+a[i]-'0')/b+'0';//求出商
            d=(d*10+(a[i]-'0'))%b;//求出余数
    }
    int p=0;
    for(int i=0;i<r.size();i++)
    if(r[i]!='0') {p=i;break;}
    return r.substr(p);
}

高精度对单精度取模

int mod(string a,int b)//高精度a除以单精度b
{
    int d=0;
    for(int i=0;i<a.size();i++) d=(d*10+(a[i]-'0'))%b;//求出余数
    return d;
}

高精度阶乘

string fac(int n)
{
    const int L=100005;
    int a[L];
    string ans;
    if(n==0) return "1";
    fill(a,a+L,0);
    int s=0,m=n;
    while(m) a[++s]=m%10,m/=10;
    for(int i=n-1;i>=2;i--)
    {
        int w=0;
        for(int j=1;j<=s;j++) a[j]=a[j]*i+w,w=a[j]/10,a[j]=a[j]%10;
        while(w) a[++s]=w%10,w/=10;
    }
    while(!a[s]) s--;
    while(s>=1) ans+=a[s--]+'0';
    return ans;
}

高精度幂

#define L(x) (1 << (x))
const double PI = acos(-1.0);
const int Maxn = 133015;
double ax[Maxn], ay[Maxn], bx[Maxn], by[Maxn];
char sa[Maxn/2],sb[Maxn/2];
int sum[Maxn];
int x1[Maxn],x2[Maxn];
int revv(int x, int bits)
{
    int ret = 0;
    for (int i = 0; i < bits; i++)
    {
        ret <<= 1;
        ret |= x & 1;
        x >>= 1;
    }
    return ret;
}
void fft(double * a, double * b, int n, bool rev)
{
    int bits = 0;
    while (1 << bits < n) ++bits;
    for (int i = 0; i < n; i++)
    {
        int j = revv(i, bits);
        if (i < j)
            swap(a[i], a[j]), swap(b[i], b[j]);
    }
    for (int len = 2; len <= n; len <<= 1)
    {
        int half = len >> 1;
        double wmx = cos(2 * PI / len), wmy = sin(2 * PI / len);
        if (rev) wmy = -wmy;
        for (int i = 0; i < n; i += len)
        {
            double wx = 1, wy = 0;
            for (int j = 0; j < half; j++)
            {
                double cx = a[i + j], cy = b[i + j];
                double dx = a[i + j + half], dy = b[i + j + half];
                double ex = dx * wx - dy * wy, ey = dx * wy + dy * wx;
                a[i + j] = cx + ex, b[i + j] = cy + ey;
                a[i + j + half] = cx - ex, b[i + j + half] = cy - ey;
                double wnx = wx * wmx - wy * wmy, wny = wx * wmy + wy * wmx;
                wx = wnx, wy = wny;
            }
        }
    }
    if (rev)
    {
        for (int i = 0; i < n; i++)
            a[i] /= n, b[i] /= n;
    }
}
int solve(int a[],int na,int b[],int nb,int ans[])
{
    int len = max(na, nb), ln;
    for(ln=0; L(ln)<len; ++ln);
    len=L(++ln);
    for (int i = 0; i < len ; ++i)
    {
        if (i >= na) ax[i] = 0, ay[i] =0;
        else ax[i] = a[i], ay[i] = 0;
    }
    fft(ax, ay, len, 0);
    for (int i = 0; i < len; ++i)
    {
        if (i >= nb) bx[i] = 0, by[i] = 0;
        else bx[i] = b[i], by[i] = 0;
    }
    fft(bx, by, len, 0);
    for (int i = 0; i < len; ++i)
    {
        double cx = ax[i] * bx[i] - ay[i] * by[i];
        double cy = ax[i] * by[i] + ay[i] * bx[i];
        ax[i] = cx, ay[i] = cy;
    }
    fft(ax, ay, len, 1);
    for (int i = 0; i < len; ++i)
        ans[i] = (int)(ax[i] + 0.5);
    return len;
}
string mul(string sa,string sb)
{
    int l1,l2,l;
    int i;
    string ans;
    memset(sum, 0, sizeof(sum));
    l1 = sa.size();
    l2 = sb.size();
    for(i = 0; i < l1; i++)
        x1[i] = sa[l1 - i - 1]-'0';
    for(i = 0; i < l2; i++)
        x2[i] = sb[l2-i-1]-'0';
    l = solve(x1, l1, x2, l2, sum);
    for(i = 0; i<l || sum[i] >= 10; i++) // 进位
    {
        sum[i + 1] += sum[i] / 10;
        sum[i] %= 10;
    }
    l = i;
    while(sum[l] <= 0 && l>0)    l--; // 检索最高位
    for(i = l; i >= 0; i--)    ans+=sum[i] + '0'; // 倒序输出
    return ans;
}
string Pow(string a,int n)
{
    if(n==1) return a;
    if(n&1) return mul(Pow(a,n-1),a);
    string ans=Pow(a,n/2);
    return mul(ans,ans);
}

高精度GCD

string add(string a,string b)
{
    const int L=1e5;
    string ans;
    int na[L]={0},nb[L]={0};
    int la=a.size(),lb=b.size();
    for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
    for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
    int lmax=la>lb?la:lb;
    for(int i=0;i<lmax;i++) na[i]+=nb[i],na[i+1]+=na[i]/10,na[i]%=10;
    if(na[lmax]) lmax++;
    for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
    return ans;
}
string mul(string a,string b)
{
    const int L=1e5;
    string s;
    int na[L],nb[L],nc[L],La=a.size(),Lb=b.size();//na存储被乘数,nb存储乘数,nc存储积
    fill(na,na+L,0);fill(nb,nb+L,0);fill(nc,nc+L,0);//将na,nb,nc都置为0
    for(int i=La-1;i>=0;i--) na[La-i]=a[i]-'0';//将字符串表示的大整形数转成i整形数组表示的大整形数
    for(int i=Lb-1;i>=0;i--) nb[Lb-i]=b[i]-'0';
    for(int i=1;i<=La;i++)
        for(int j=1;j<=Lb;j++)
        nc[i+j-1]+=na[i]*nb[j];//a的第i位乘以b的第j位为积的第i+j-1位(先不考虑进位)
    for(int i=1;i<=La+Lb;i++)
        nc[i+1]+=nc[i]/10,nc[i]%=10;//统一处理进位
    if(nc[La+Lb]) s+=nc[La+Lb]+'0';//判断第i+j位上的数字是不是0
    for(int i=La+Lb-1;i>=1;i--)
        s+=nc[i]+'0';//将整形数组转成字符串
    return s;
}
int sub(int *a,int *b,int La,int Lb)
{
    if(La<Lb) return -1;//如果a小于b,则返回-1
    if(La==Lb)
    {
        for(int i=La-1;i>=0;i--)
            if(a[i]>b[i]) break;
            else if(a[i]<b[i]) return -1;//如果a小于b,则返回-1
 
    }
    for(int i=0;i<La;i++)//高精度减法
    {
        a[i]-=b[i];
        if(a[i]<0) a[i]+=10,a[i+1]--;
    }
    for(int i=La-1;i>=0;i--)
        if(a[i]) return i+1;//返回差的位数
    return 0;//返回差的位数
 
}
string div(string n1,string n2,int nn)//n1,n2是字符串表示的被除数,除数,nn是选择返回商还是余数
{
    const int L=1e5;
    string s,v;//s存商,v存余数
     int a[L],b[L],r[L],La=n1.size(),Lb=n2.size(),i,tp=La;//a,b是整形数组表示被除数,除数,tp保存被除数的长度
     fill(a,a+L,0);fill(b,b+L,0);fill(r,r+L,0);//数组元素都置为0
     for(i=La-1;i>=0;i--) a[La-1-i]=n1[i]-'0';
     for(i=Lb-1;i>=0;i--) b[Lb-1-i]=n2[i]-'0';
     if(La<Lb || (La==Lb && n1<n2)) {
            //cout<<0<<endl;
     return n1;}//如果a<b,则商为0,余数为被除数
     int t=La-Lb;//除被数和除数的位数之差
     for(int i=La-1;i>=0;i--)//将除数扩大10^t倍
        if(i>=t) b[i]=b[i-t];
        else b[i]=0;
     Lb=La;
     for(int j=0;j<=t;j++)
     {
         int temp;
         while((temp=sub(a,b+j,La,Lb-j))>=0)//如果被除数比除数大继续减
         {
             La=temp;
             r[t-j]++;
         }
     }
     for(i=0;i<L-10;i++) r[i+1]+=r[i]/10,r[i]%=10;//统一处理进位
     while(!r[i]) i--;//将整形数组表示的商转化成字符串表示的
     while(i>=0) s+=r[i--]+'0';
     //cout<<s<<endl;
     i=tp;
     while(!a[i]) i--;//将整形数组表示的余数转化成字符串表示的</span>
     while(i>=0) v+=a[i--]+'0';
     if(v.empty()) v="0";
     //cout<<v<<endl;
     if(nn==1) return s;
     if(nn==2) return v;
}
bool judge(string s)//判断s是否为全0串
{
    for(int i=0;i<s.size();i++)
        if(s[i]!='0') return false;
    return true;
}
string gcd(string a,string b)//求最大公约数
{
    string t;
    while(!judge(b))//如果余数不为0,继续除
    {
        t=a;//保存被除数的值
        a=b;//用除数替换被除数
        b=div(t,b,2);//用余数替换除数
    }
    return a;
}

高精度进制转换

//将字符串表示的10进制大整数转换为m进制的大整数
//并返回m进制大整数的字符串
bool judge(string s)//判断串是否为全零串
{
    for(int i=0;i<s.size();i++)
        if(s[i]!='0') return 1;
    return 0;
}
string solve(string s,int n,int m)//n进制转m进制只限0-9进制,若涉及带字母的进制,稍作修改即可
{
    string r,ans;
    int d=0;
    if(!judge(s)) return "0";//特判
    while(judge(s))//被除数不为0则继续
    {
        for(int i=0;i<s.size();i++)
        {
            r+=(d*n+s[i]-'0')/m+'0';//求出商
            d=(d*n+(s[i]-'0'))%m;//求出余数
        }
       s=r;//把商赋给下一次的被除数
       r="";//把商清空
        ans+=d+'0';//加上进制转换后数字
        d=0;//清空余数
    }
    reverse(ans.begin(),ans.end());//倒置下
    return ans;
}

高精度求平方根

const int L=2015;
string add(string a,string b)//只限两个非负整数相加
{
    string ans;
    int na[L]={0},nb[L]={0};
    int la=a.size(),lb=b.size();
    for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
    for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
    int lmax=la>lb?la:lb;
    for(int i=0;i<lmax;i++) na[i]+=nb[i],na[i+1]+=na[i]/10,na[i]%=10;
    if(na[lmax]) lmax++;
    for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
    return ans;
}
string sub(string a,string b)//只限大的非负整数减小的非负整数
{
    string ans;
    int na[L]={0},nb[L]={0};
    int la=a.size(),lb=b.size();
    for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
    for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
    int lmax=la>lb?la:lb;
    for(int i=0;i<lmax;i++)
    {
        na[i]-=nb[i];
        if(na[i]<0) na[i]+=10,na[i+1]--;
    }
    while(!na[--lmax]&&lmax>0)  ;lmax++;
    for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
    return ans;
}
string mul(string a,string b)//高精度乘法a,b,均为非负整数
{
    string s;
    int na[L],nb[L],nc[L],La=a.size(),Lb=b.size();//na存储被乘数,nb存储乘数,nc存储积
    fill(na,na+L,0);fill(nb,nb+L,0);fill(nc,nc+L,0);//将na,nb,nc都置为0
    for(int i=La-1;i>=0;i--) na[La-i]=a[i]-'0';//将字符串表示的大整形数转成i整形数组表示的大整形数
    for(int i=Lb-1;i>=0;i--) nb[Lb-i]=b[i]-'0';
    for(int i=1;i<=La;i++)
        for(int j=1;j<=Lb;j++)
        nc[i+j-1]+=na[i]*nb[j];//a的第i位乘以b的第j位为积的第i+j-1位(先不考虑进位)
    for(int i=1;i<=La+Lb;i++)
        nc[i+1]+=nc[i]/10,nc[i]%=10;//统一处理进位
    if(nc[La+Lb]) s+=nc[La+Lb]+'0';//判断第i+j位上的数字是不是0
    for(int i=La+Lb-1;i>=1;i--)
        s+=nc[i]+'0';//将整形数组转成字符串
    return s;
}
int sub(int *a,int *b,int La,int Lb)
{
    if(La<Lb) return -1;//如果a小于b,则返回-1
    if(La==Lb)
    {
        for(int i=La-1;i>=0;i--)
            if(a[i]>b[i]) break;
            else if(a[i]<b[i]) return -1;//如果a小于b,则返回-1
 
    }
    for(int i=0;i<La;i++)//高精度减法
    {
        a[i]-=b[i];
        if(a[i]<0) a[i]+=10,a[i+1]--;
    }
    for(int i=La-1;i>=0;i--)
        if(a[i]) return i+1;//返回差的位数
    return 0;//返回差的位数
 
}
string div(string n1,string n2,int nn)//n1,n2是字符串表示的被除数,除数,nn是选择返回商还是余数
{
    string s,v;//s存商,v存余数
     int a[L],b[L],r[L],La=n1.size(),Lb=n2.size(),i,tp=La;//a,b是整形数组表示被除数,除数,tp保存被除数的长度
     fill(a,a+L,0);fill(b,b+L,0);fill(r,r+L,0);//数组元素都置为0
     for(i=La-1;i>=0;i--) a[La-1-i]=n1[i]-'0';
     for(i=Lb-1;i>=0;i--) b[Lb-1-i]=n2[i]-'0';
     if(La<Lb || (La==Lb && n1<n2)) {
            //cout<<0<<endl;
     return n1;}//如果a<b,则商为0,余数为被除数
     int t=La-Lb;//除被数和除数的位数之差
     for(int i=La-1;i>=0;i--)//将除数扩大10^t倍
        if(i>=t) b[i]=b[i-t];
        else b[i]=0;
     Lb=La;
     for(int j=0;j<=t;j++)
     {
         int temp;
         while((temp=sub(a,b+j,La,Lb-j))>=0)//如果被除数比除数大继续减
         {
             La=temp;
             r[t-j]++;
         }
     }
     for(i=0;i<L-10;i++) r[i+1]+=r[i]/10,r[i]%=10;//统一处理进位
     while(!r[i]) i--;//将整形数组表示的商转化成字符串表示的
     while(i>=0) s+=r[i--]+'0';
     //cout<<s<<endl;
     i=tp;
     while(!a[i]) i--;//将整形数组表示的余数转化成字符串表示的</span>
     while(i>=0) v+=a[i--]+'0';
     if(v.empty()) v="0";
     //cout<<v<<endl;
     if(nn==1) return s;
     if(nn==2) return v;
}
bool cmp(string a,string b)
{
    if(a.size()<b.size()) return 1;//a小于等于b返回真
    if(a.size()==b.size()&&a<=b) return 1;
    return 0;
}
string DeletePreZero(string s)
{
    int i;
    for(i=0;i<s.size();i++)
        if(s[i]!='0') break;
    return s.substr(i);
}

string BigInterSqrt(string n)
{
    n=DeletePreZero(n);
    string l="1",r=n,mid,ans;
    while(cmp(l,r))
    {
        mid=div(add(l,r),"2",1);
        if(cmp(mul(mid,mid),n)) ans=mid,l=add(mid,"1");
        else r=sub(mid,"1");
    }
    return ans;
}

29.约瑟夫

最后一个出列的人

#include <iostream>
using namespace std;
int main()
{
	int n,c;
	cout<<"Input n:"<<endl;
	cin>>n;
	cout<<"Input c:"<<endl;
	cin>>c;
	int pos=1;
	for (int i=2;i<=n;i++)
		pos=(pos+c-1)%i+1;
	cout<<pos<<endl;
	return 0;
}

第k个人

#include <iostream>
using namespace std;
int main()
{
	int n,c,k;
	cout<<"Input n:"<<endl;
	cin>>n;
	cout<<"Input c:"<<endl;
	cin>>c;
	cout<<"Input k:"<<endl;
	cin>>k;
	k=n+1-k;
	int pos=(c-1)%k+1;
	for (int i=k+1;i<=n;i++)
		pos=(pos+c-1)%i+1;
	cout<<pos<<endl;
	return 0;
}

知识补充
现在要求输出第k个出列的人的编号。
规模为i-1的队列的任意一人的编号为p,规模为i的队列的任意一人的编号为q。
则q=(p+c-1)%i+1。
另外,第k个出列的人出列前,队列规模为N+1-k。并且此人在这个队列中的编号为C mod (N+1-k),同样要改进一下,变为(C-1) mod (N+1-k)+1。
这样,我们一步步递推,便可以得出此人本来的编号。

我们知道,约瑟夫环的出队是有O(n)的递推算法的:f(n) = (f(n-1)+k-1)%n
考虑一下n个人第m个出列,设状态为f(n,m),我们可以假设在m个人中再插上n-m个人,他们都比前m个人晚出队(具体放在哪里不用关心,只要认为他们一定不会先出队就行了),那么递推式就和刚刚的雷同 f(n,m) = (f(n-1,m-1)+k-1)%n,这个式子可以在o(m)的时间内求出答案,适用于m≤1e6的情况
那么当m在1e18的范围内,该怎么办呢?
观察当前递推式,每次都是+k,当超过n的范围时,进行取模运算,可以发现,当k<<n时,在很多次递推操作中都是不需要取模的,而是只有+k操作,那么我们其实可以吧加法转化成乘法来加速
令add = n-m,考虑当前为f(x+add,x)执行t次后需要取模:
f(x+add+t,x+t) = (f(x+add,x) + (tk)-1)%(x+add+t)+1
为什么这里的模数在一直变化,还可以这么干呢?因为模数每次只增加1,而f每次增加k,所以当k>1的时候,一定会越来越接近模数,直到超过它,当k=1的时候特判就好
计算t值:
一下简写f(x+add,x)为f
f + (t
k)-1 ≥ x+add+t
解得 t ≥ (x+add-f+1) / (k-1)
从这里也可以看出k=1是需要特判的
解出最小t,更新状态 x = x + t
这个复杂度我也不会算了,但感觉不会太大

第K个人优化

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    ll n,m,k;
    int t;
    scanf("%d",&t);
    for(int g = 1;g <= t;++g){
        scanf("%lld%lld%lld",&n,&m,&k);
        ll ans = (k-1) % (n-m+1);
        if(k == 1) ans = m-1;
        else{
            for(ll i = n-m+2;i <= n;++i){
                ans = (ans+k) % i;
                ll temp = i-ans-1;
                temp /= k;
                temp--;
                if(n-i-1 < temp) temp = n-1-i;
                if(temp > 0){
                    i += temp;
                    ans = ans+k*temp;
                }
            }
        }
        printf("Case #%d: %lld\n",g,ans+1ll);
    }
    return 0;
}

数位dp模板

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll l,r;
int a[20];
//dp[i][j]表示高一位为j的i位数满足条件的个数
ll dp[20][20];
//pos:当前枚举数位 pre:上一位数 status:是否有前导零(0为有) limit:是否有上限限制
int dfs(int pos,int pre,int status,bool limit){
    if(pos<0){
        return 1;
    }
    if(status && !limit && dp[pos][pre]!=-1){
        return dp[pos][pre];
    }
    //是否有上限限制
    int up=limit?a[pos]:9;
    ll ans=0;
    if(status==0){
        for(int i=0;i<=up;i++){
            ans+=dfs(pos-1,i,i,limit&&(i==up));
        }
    }else{
        for(int i=0;i<=up;i++){
            if(abs(i-pre)<2){
                continue;
            }
            ans+=dfs(pos-1,i,1,limit&&(i==up));
        }
    }
    if(status && !limit){
        dp[pos][pre]=ans;
    }
    return ans;
}
int solve(ll x){
    int len=0;
    while(x){
        a[len++]=x%10;
        x/=10;
    }
    memset(dp,-1,sizeof(dp));
    return dfs(len-1,0,0,true);
}
int main(void){
    scanf("%lld%lld",&l,&r);
    int ans=solve(r)-solve(l-1);
    printf("%d\n",ans);
    return 0;
}

bzoj1026 找出区间内满足相邻两位数差大于等于2的数的个数

#include <map>
#include <queue>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#define IN freopen("in.txt","r",stdin)
#define OUT freopen("out.txt","w",stdout)
#define IO do{\
    ios::sync_with_stdio(false);\
    cin.tie(0);\
    cout.tie(0);}while(0)
using namespace std;
typedef long long ll;
const int maxn =  1e4+10;
const int MAXN = 1e6+10;
const int INF = 0x3f3f3f3f;
const int inf = 0x3f;
const double EPS = 1e-7;
const double Pi = acos(-1);
const int MOD = 1e9+7;
int dp[15][15];
int num[15];
void init()
{
    dp[0][0] = 1;
    for(int i=1; i<=9; i++)
        for(int j=0; j<=9; j++)
        {
            if(j==4)
                dp[i][j] = 0;
            else
                for(int k=0; k<=9; k++)
                {
                    if(j==6&&k==2)
                        continue;
                    dp[i][j] += dp[i-1][k];
                }
        }
}
int ask(int x)
{
    ll ans = 0;
    int cnt = 0;
    while(x)
        num[++cnt] = x%10,x/=10;
    num[cnt+1] = 0;
    for(int i=cnt; i>=1; i--)
    {
        for(int j=0; j<num[i]; j++)
        {
            if(j==4||(j==2&&num[i+1]==6))
                continue;
            ans += dp[i][j];
        }
        if(num[i] == 4||(num[i]==2&&num[i+1]==6))
            break;
    }
    return ans;
}
int main()
{
    IO;
    //IN;
    int n,m;
    init();
    while(cin >> m >>n &&(n||m))
        cout <<ask(n+1)-ask(m)<<endl;
    return 0;

}

不要62

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

int k,bit[25];
ll dp[25][1 << 10][11];

int getnew(int x,int s){  ##  一个更新01二进制状压的函数
	for(int i = x;i < 10;i++){  ##  从x(0~9)开始找,看有没有比他大的数字,有的话就进行替换
		if(s & (1 << i)) return (s^(1 << i)) | (1 << x);  ##  替换操作
	}
	return s | (1 << x);  ##  如果没有,则代表他是上升子序列里一个新的高度,则添加进上升子序列
}

int getn(int s){  ##  计算这个01二进制传里有多少个1,就是计算上升子序列的长度
	int cnt = 0;
	while(s){
		if(s & 1) cnt++;
		s >>= 1;
	}
	return cnt;
}

ll dfs(int pos,int sta,bool f,bool limit){  ##  标准的数位dp模板,pos代表位数,sta代表当前01二进制串的状态,f表示是否前面位数都是先导0,limit则是前面位数是否是上限
	if(pos == -1) return getn(sta) == k;  ##  位数到底,计算状态长度并返回
	if(!limit && dp[pos][sta][k] != -1) return dp[pos][sta][k];  ##  如果不是上限值下的次位,因为我们已经存起来了,所以可以直接返回值(记忆化搜索)
	int end = limit ?bit[pos] :9;  ##  根据这位是否是上限,来决定下一位的值
	
	ll sum = 0;  ##  求和,计算pos-1位下总共有多少个上升子序列长度为k的数字
	for(int i = 0;i <= end;i++) sum += dfs(pos-1,(f && !i) ?0:getnew(i,sta),f && !i,limit && (i == bit[pos]));  ##  这里一定要注意,如果全是先导0,我们要给他0的状态 ,直到他遇上非0的数字,先导0和后面的0性质是不一样的,这里可以自己思考一下
	if(!limit) dp[pos][sta][k] = sum;  ##  记忆化搜索,不然会超时
	return sum;  ##  返回统计出来的值
}

ll cal(ll x){  ##  把输入的值转换为数组,以进行数位dp
	int len = 0;
	while(x){
		bit[len++] = x % 10;
		x /= 10;
	}
	return dfs(len - 1,0,1,1);
}

int main()
{
	int t;
	scanf("%d",&t);
	memset(dp,-1,sizeof(dp));
	for(int i = 1;i <= t;i++){
		long long l,r;
		scanf("%lld%lld%d",&l,&r,&k);
		printf("Case #%d: ",i);
		printf("%lld\n",cal(r) - cal(l - 1));  ##  算出1到R范围内有多少个k长度的值再减去1到l - 1范围内的,类似一种前缀和的思想
	}
	return 0;
} 

在这里插入图片描述

30.母函数

普通型生成函数用于解决多重集的组合问题,而指数型母函数用于解决多重集的排列问题。

就是把一个已知的序列和x的多项式合并起来,新产生的多项式就叫原来序列的母函数
序列{0,1,2,3,4,5…n}的母函数就是
f(x)=0+x+2x2+3x3+4x4+…+nxn(这个x没有任何意义,应该说,你不需要把它当做一个函数,你只要知道母函数这么写就可以了)

序列{1,1,1,1,1…}的母函数就是
f(x)=1+x+x2+x3+x^4…

二项式展开的序列比如这个{1,4,6,4,1,0,0,0,0,0…}是C(4,0)到C(4,4)的系数,那它的母函数就是
f(x)=1+4x+6x2+4x3+1x^4

这些东西对于我们来说并没有说明意义,母函数对我们来说只是一个工具,是一个载体,那来看看母函数是怎么样装载数据的吧!

例1:若有1克、2克、3克、4克的砝码各一 枚,能称出哪几种重量?各有几种可能方案?
假如x的指数表示砝码
那么

1克的砝码表示为1+x^1
2克的砝码表示为1+x^2
3克的砝码表示为1+x^3
4克的砝码表示为1+x^4

每个砝码都可以选择取或不取
所以这里的1可以认为1*x^0,表示不取这颗砝码
那么把这些乘起来
(1+x1)(1+x2)(1+x3)(1+x4)
=1+(x1)+(x2)+2(x3)+2(x4)+2(x5)+2(x6)+2(x7)+(x8)+(x9)+(x10)
根据指数来看,我们可以称出010这么多的重量,其中37的系数为2,说明有2种称的方法
那么我们来细看一遍

0:(什么砝码都不放)…(1种)
1:1…(1种)
2:2…(1种)
3:3或1+2…(2种)
4:4或1+3…(2种)
5:1+4或2+3…(2种)
6:2+4或1+2+3…(2种)
7:3+4或1+2+4…(2种)
8:1+3+4…(1种)
9:2+3+4…(1种)
10:1+2+3+4…(1种)

分毫不差
所以说母函数在ACM就是这么用的,跟函数没关系,跟写法有关系。
例2:求用1分、2分、3分的邮票贴出不同数值的方案数,每种邮票可以有无限张。
因邮票允许重复,故母函数为:
对于这种连续相乘,我们人类是很讨厌的,但对于计算机这种重复运算是很轻松的,我们只要将我们解多项式的思路带进去即可。

例二

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAX 310
int a[MAX];///最终合并的多项式
int b[MAX];///临时合并用的多项式
int n;
const int N=110;
using namespace std;
void init()
{
    int i,j,k;
    a[0]=1;///一开始的0的情况
    for (i=1; i<=3; i++)///代表1分,2分,3分三个多项式的合并
    {
        for (j=0; j<N; j+=i)///i分的邮票,步长为i
        {
            for (k=0; k+j<N; k++)///从x^0到x^N遍历一遍
            {
                b[j+k]+=a[j];///核心
            }
        }
        for (j=0; j<N; j++)///b中数据抄到a,b清空
        {
            a[j]=b[j];
            b[j]=0;
        }
    }
}
int main()
{
    int i,j,k;
    init();
    while(scanf("%d",&n)!=EOF)
    {
        printf("%d\n",a[n]);
    }
    return 0;
}

裸板


#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
 
const int maxn = 1e5 + 10;
#define mem(a) memset(a, 0, sizeof a)
int a[maxn],b[maxn];
 
int s1[maxn],s2[maxn];
 
void init() {
    mem(a);
    mem(b);
    mem(s1);
    mem(s2);
}
 
int main() {
    int n,m;
    while (~scanf("%d%d", &n, &m)) {
       init();
       for (int i = 0; i < n; i++) {
            scanf("%d%d", &s1[i], &s2[i]);
       }
        for (int i = s1[0]; i <= s2[0]; i++) a[i] = 1;
        for (int i = 1; i < n; i++) {
            mem(b);
            for (int j = 0; j <= m; j++) {
                for (int k = s1[i]; k <= s2[i]; k++) {
                    b[j + k] += a[j];
                }
            }
            memcpy(a, b, sizeof b);
        }
        printf("%d\n", a[m]);
    }
    return 0;
}

指数函数:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
裸板


#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
 
const int maxn = 1e5 + 10;
#define mem(a) memset(a, 0, sizeof a)
 
double a[maxn],b[maxn]; // 注意为浮点型
 
int s1[maxn];
 
double f[11];
void init() {
    mem(a);
    mem(b);
    mem(s1);
    f[0] = 1;
    for (int i = 1; i <= 10; i++) {
        f[i] = f[i - 1] * i;
    }
}
 
int main() {
    int n,m;
    while (~scanf("%d%d", &n, &m)) {
       init();
       for (int i = 0; i < n; i++) {
            scanf("%d", &s1[i]);
       }
        for (int i = 0; i <= s1[0]; i++) a[i] = 1.0 / f[i];
        for (int i = 1; i < n; i++) {
            mem(b);
            for (int j = 0; j <= m; j++) {
                for (int k = 0; k <= s1[i] && k + j <= m; k++) {
                    b[j + k] += a[j] * 1.0 / f[k]; //注意这里
                }
            }
            memcpy(a, b, sizeof b);
        }
       printf("%.0f\n", a[m] * f[m]);
    }
    return 0;
}

31.背包dp

分组背包(取对数缩小规模)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int prime[3005],temp[3005];
double lg[3005];

void prime_table(){
	memset(lg,0,sizeof(lg));
    for(int i = 2;i <= 3000;i++){
        if (!temp[i]){
        	prime[++prime[0]] = i;
        	lg[prime[0]] = log(i * 1.0);
		}
        for (int j = 1;j <= prime[0] && i*prime[j] <= 3000;j++){
            temp[i*prime[j]] = i;
            if(i % prime[j] == 0) break;
        }
    }
    return;
}

int main()
{
	prime_table();
	int n,m;
	
	while(cin >> n >> m){
		double dp[3005] = {0};
		int ans[3005];
		for(int i = 0;i <= n;i++) ans[i] = 1;
		
		for(int i = 1;i <= prime[0] && prime[i] <= n;i++){
			for(int j = n;j >= prime[i];j--){
				for(int k = prime[i],cnt = 1;k <= j;k *= prime[i],cnt++){
					if(dp[j - k] + lg[i]*cnt > dp[j]){
						dp[j] = dp[j - k] + lg[i]*cnt;
						ans[j] = (ans[j - k]*k) % m;
					}
				}
			}
		}
		
		cout << ans[n] << endl;
	}
	return 0;
} 

依赖背包
主件附件先处理为集合

#include <bits/stdc++.h>
using namespace std;
 
vector <int> s[61];
 
int main()
{
    int n,m,cnt = 0;
    int q,v[65],p[65],vis[65] = {0};
    int dp[40000],temp[40000];
    memset(dp,0,sizeof(dp));
    memset(temp,0,sizeof(temp));
     
    cin >> n >> m;
    for(int i = 1;i <= m;i++){
        cin >> v[i] >> p[i] >> q;
        p[i] *= v[i];
        if(q){
            s[q].push_back(i);
            vis[i] = 1;
        }
    }
     
    for(int i = 1;i <= m;i++){
        if(!vis[i]){
            if(!s[i].size()){
                for(int j = n;j >= v[i];j--){
                    dp[j] = max(dp[j],dp[j - v[i]] + p[i]);
                }
            }
            else{
                memcpy(temp,dp,sizeof(temp));
                for(int j = 0;j < s[i].size();j++){
                    for(int k = n - v[i];k >= v[s[i][j]];k--){
                        temp[k] = max(temp[k],temp[k - v[s[i][j]]] + p[s[i][j]]);
                    }
                }
                for(int j = v[i];j <= n;j++) dp[j] = max(dp[j],temp[j - v[i]] + p[i]);
            }
        }
    }
     
    cout << dp[n] << endl;
    return 0;
}

分组背包记录路径

#include <bits/stdc++.h>
using namespace std;
 
int dp[5050],p[1050],c[1050],w[1050][550],d[1050][550];
 
int main()
{
    int n,m;
    memset(d,0,sizeof(d));
    memset(dp,0,sizeof(dp));
     
    cin >> n >> m;
    for(int i = 1;i <= n;i++){
        cin >> c[i] >> p[i];
        for(int j = 1;j <= p[i];j++) cin >> w[i][j];
    }
     
    for(int i = 1;i <= n;i++){
        for(int j = m;j >= 0;j--){
            for(int k = 0;k <= p[i];k++){
                if(k*c[i] > j) break;
                else{
                    if(dp[j] < dp[j - k*c[i]] + w[i][k]){
                        dp[j] = dp[j - k*c[i]] + w[i][k];
                        d[i][j] = k;
                    }
                }
            } 
        }
    }
     
    cout << dp[m] << endl;
     
    int idx,maxs = 0;
    for(int i = m;i >= 1;i--){
        if(maxs <= dp[i]){
            maxs = dp[i];
            idx = i;
        }
    }
     
    int path[1050] = {0};
    for(int i = n;i >= 1;i--){
        path[i] = d[i][idx];
        idx -= path[i]*c[i];
    }
     
    for(int i = 1;i <= n;i++){
        cout << path[i] << endl;
    }
    return 0;
} 

混合背包(二进制优化)

#include <bits/stdc++.h>
using namespace std;
 
int dp[105][105],n,p,r;
 
void add(int w1,int w2,int s,int v){
    if(!s){
        for(int i = w1;i <= p;i++){
            for(int j = w2;j <= r;j++){
                dp[i][j] = max(dp[i][j],dp[i - w1][j - w2] + v);
            }
        }
    }
    else{
        int k = 1;
        while(k <= s){
            for(int i = p;i >= w1*k;i--){
                for(int j = r;j >= w2*k;j--){
                    dp[i][j] = max(dp[i][j],dp[i - w1*k][j - w2*k] + v*k);
                }
            }
            s -= k;
            k <<= 1;
        }
         
        for(int i = p;i >= w1*s;i--){
            for(int j = r;j >= w2*s;j--){
                dp[i][j] = max(dp[i][j],dp[i - w1*s][j - w2*s] + v*s);
            }
        }
    }
     
    return;
}
 
int main()
{
    cin >> n >> p >> r;
     
    memset(dp,0,sizeof(dp));
    int w1,w2,s,v;
    for(int i = 1;i <= n;i++){
        cin >> w1 >> w2 >> s >> v;
        add(w1,w2,s,v);
    }
     
    cout << dp[p][r] << endl;
    return 0;
} 

混合背包单调队列优化模板

#include<bits/stdc++.h>
using namespace std;
const int Max = 1e5+10;

int n, v;
int val[Max], vol[Max], num[Max];
int que[Max];
int dp[Max];

void work()
{
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= n; i++)
    {
        if (num[i] == 1)                    //01背包
        {
            for (int j = v; j >= vol[i]; j--)
                dp[j] = max(dp[j], dp[j - vol[i]] + val[i]);
            continue;
        }
        if (vol[i] * num[i] >= v)            //完全背包,这个比较关键,因为用单调队列处理非常耗时
        {
            for (int j = vol[i]; j <= v; j++)
                dp[j] = max(dp[j], dp[j - vol[i]] + val[i]);
            continue;
        }
        for (int res = 0; res < vol[i]; res++)                            //枚举余数
        {
            int head = 0, tail = -1;
            for (int k = 0; k <= (v - res) / vol[i]; k++)                //枚举k',即等差数列的项数,每次更新同余数的数
                //因为只有余数相同的数才会互相影响
            {
                int value = dp[k * vol[i] + res] - k * val[i];            //当前的价值

                if (tail - head == k)                                    //就算用尽所有材料也无法从que[head]转移
                    head++;
                while (head <= tail && que[tail] <= value)                //取最大值
                    tail--;
                tail++;
                que[tail] = value;
                dp[k * vol[i] + res] = que[head] + k * val[i];
            }
        }
    }
}

32.附件

java大数加法

import java.util.*;
import java.math.*;

public class Main{
	public static void main(String[] args){
		Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        while(n-- > 0){
            BigInteger ans = new BigInteger("0");
            while(input.hasNextBigInteger()){
                BigInteger temp = new BigInteger("0");
                temp = input.nextBigInteger();
                if(!temp.equals(BigInteger.valueOf(0)) ){
                    ans = ans.add(temp);
                }
                else{
                    System.out.println(ans);
                    if(n != 0) System.out.println();
                    break;
                }
            }
        }
        input.close();
	}
}

大浮点数加法

import java.util.*;
import java.math.*;

public class Main{
	public static void main(String[] args){
		Scanner input = new Scanner(System.in);
		BigDecimal base;
        while(input.hasNextBigDecimal()){
              base = input.nextBigDecimal();
              int n = input.nextInt();
              String ans;
              base = base.pow(n);
              ans = base.stripTrailingZeros().toPlainString();
              if(ans.startsWith("0.")) ans = ans.substring(1);
              System.out.println(ans);
        }
        input.close();
	}
}

java斐波那契

import java.util.*;
import java.math.*;

public class Main{
	public static void main(String[] args){
		Scanner in = new Scanner(System.in);
		BigInteger [] f = new BigInteger[1005];
		f[1] = new BigInteger("1");
		f[2] = new BigInteger("2");
		for(int i = 3;i < 1005;i++){
            f[i] = f[i - 1].add(f[i - 2]);
		}

        while(in.hasNext()){
            BigInteger a = in.nextBigInteger();
            BigInteger b = in.nextBigInteger();
            BigInteger zero = new BigInteger("0");

            if(a.compareTo(zero) == 0 && b.compareTo(zero) == 0) break;
            int sum = 0;
            for(int i = 1;i < 1005;i++){
                if(f[i].compareTo(a) < 0) continue;
                if(f[i].compareTo(b) > 0) break;
                sum++;
            }
            System.out.println(sum);
        }
        in.close();
	}
}

java阶乘

mport java.util.*;
import java.math.*;

public class Main{
	public static void main(String[] args){
		Scanner input = new Scanner(System.in);
//		double a = input.nextDouble();
//		double b = input.nextDouble();
        while(input.hasNext()){
            int n = input.nextInt();
            BigInteger ans = BigInteger.ONE;
            for(int i = 1;i <= n;i++){
                ans = ans.multiply(BigInteger.valueOf(i));
            }
            System.out.println(ans);
        }
	}
}

python

import math

m = 10000
p,a = [],[]

for i in range(0,m+1):
	a.append(1)
a[1] = a[0] = 0

for i in range(0,m+1):
	if a[i]:
		p.append(i)
	for j in p:
		if i*j > m:break
		a[i*j] = 0
		if not i%j: break
		
T = int(input())
for o in range(0,T):
	n = int(input())
	h,k = 1,1
	for i in p:
		if h*i > n:break
		h *= i
		k *= (i+1)
	
	g = math.gcd(h,k)
	print(h // g,end = '/')
	print(k // g)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值