ACM模板四:代数、快速幂、数论、组合

目录

〇,全文说明、宏定义代码

一,代数

1,代码

2,测试代码

二,单例、数论

1,代码

2,测试代码

三,快速幂、BSGS、逆元、组合数、全排列、容斥原理

1,代码

2,测试代码

四,排列组合

1,代码

2,测试代码


〇,全文说明、宏定义代码

类里面和宏定义处都有接口注释,因为宏不体现具体参数,所以注释以类里面的为准。

代码工程结构:

  • 每一章的第一节《代码》可以独立编译运行(本地要加LOCAL宏)
  • 每一章的第二节《测试代码》搭配第〇章的代码和本章第一节的代码可以编译运行并测试成功
  • 所有模板文章的第〇章的代码和其他章第一节的《代码》全部汇总起来可以编译运行

宏定义代码:

///(1)代数///
#define MinSumLen Algebra::minSumLen//最小距离问题,给出数轴上若干点,求出到任意点的总距离的最小值
#define EverSumLen Algebra::everSumLen//最小距离问题衍生问题,给出数轴上若干点,求出[low,high]段所有整点到所有点的总距离
// CloseInterval 略。闭区间的有序集合
#define GetLowBit Bits::getLowBit //二进制最低位
#define GetHighBit Bits::getHighBit//二进制最高位
#define IsPowerOf2 Bits::isPowerOf2 //是否是2的幂
#define GetBitLength Bits::getBitLength//二进制总位数
#define FgetNum1 Bits::getNum1 //二进制中1的个数
#define FgetNum0 Bits::getNum0 //二进制中0的个数,只算最高位1后面的0
#define GetReverseNum Bits::getReverseNum //二进制反转,01互换,仅限最高位1后面的位
#define AllExceptOneMin VecSemiGroup::allExceptOneMin//枚举只去掉1个数,剩下的数的最小值
#define AllExceptOneOr VecSemiGroup::allExceptOneOr//枚举只去掉1个数,剩下的数的或运算

///(2.1)单例///
// SingleA 略。单例模板

///(2.2)数论基础///
#define NumInRadix NumberTheory::numInRadix //一个数在d进制下的位数
#define IntToStr NumberTheory::intToStr //把整数转化为字符串,返回值是字符串长度
#define StrToInt NumberTheory::strToInt //把字符串转化为整数
#define IsPrime NumberTheory::isPrime // 素数检测
#define GetHighNum NumberTheory::getHighNum //把3245分解成3*1000+245,出参a=3 b=1000 c=245
#define IsHas9 NumberTheory::isHas9 //一个十进制数里面有没有数字9
#define NumHas9 NumberTheory::numHas9 //1-n中有多少十进制数里面有数字9,n<10^9
#define IsPalindrome NumberTheory::isPalindrome //是否为回文数
#define Gcd NumberTheory::gcd //最大公约数
#define Lcm NumberTheory::lcm //最小公倍数

///(2.3)数论进阶///
#define IsPrime2 Sieve::GetSingleA().isPrime//判断n是不是素数
#define GetPrime Sieve::GetSingleA().getPrime//获取[1,n]内的所有素数
#define Fenjie Facs::fenjie //因式分解
#define Fenjie2 Facs::fenjie2 //积性分解,即单素数幂分解
#define GetFacs Facs::getFacs //获取所有因子
#define GetMaxFacs Facs::getMaxFacs//获取最大因子
#define GetDivisors Facs::getDivisors//获取所有约数
#define FindNear Facs::findNear//在x的所有约数中,寻找离a最近的一个
#define IsSquareSum Facs::isSquareSum//判断是不是平方和
#define GetPhi Phi::getPhi//欧拉函数
#define GetFacsOfPhi Phi::getFacsOfPhi//计算phi(n)的所有因子facs,返回phi(n)
#define GetOrder Order::getOrder//求a mod n的阶
#define FactorialDegree Factorial::degree //求m!中素数p的次数
#define FactorialDegree2 Factorial::degree2 //求m!中n的次数
#define FactorialFactorization Factorial::factorization //求n!的因式分解

///(3.1)快速乘法、幂、矩阵幂///
// 实现了泛埃及乘法,并泛化成了快速乘法、快速幂、矩阵快速幂。
#define MultiAdd Multi::multiAdd//快速乘法
#define MultiMulti Multi::multiMulti//快速幂
#define MultiMultiAdd Multi::multiMultiAdd//快速幂套快速乘法
#define MultiMatrix Multi::multiMatrix//矩阵快速幂

///(3.2)BSGS、逆元///
#define DiscreteLog BSGS::discreteLog//求离散对数a^x=b(mod p)
#define GetInverseNum InverseNum().getInverseNum //求逆元

///(3.3)组合数、全排列、容斥原理///
#define GetAllComValue ComNum<>::getAllValue //所有组合数C(i,j)%p,0<=i<=n,0<=j<=k
// ComNum其他函数,略。 // 组合数、阶乘等,单例方式调用
// Permutations,略。 // 全排列,单例方式调用
// InclusionExclusion,略。// 容斥原理
// IECombina,略。//在n个球中可重复的选s次,求其中b个球至少被选中一次的情况数
// IECombina2,略。//在n个球中可重复的选s次,每相邻k次之中不能有重复的球,求其中每个球至少被选中一次的情况数
// IECombina3,略。 //从1到n有多少数是v中任一数的倍数

///(4.1)排列组合///
#define GetAllPermutation Combina::getAllPermutation //在一个列表中选出若干个数,得到所有排列,按照id的字典序来排序
#define GetAllCombina Combina::getAllCombina//在一个列表中选出若干个数,得到所有组合,按照id的字典序来排序
#define IntSplitA Combina::intSplitA//把s拆分成n个不同数的和, 返回所有排列
#define IntSplitC Combina::intSplitC//把s拆分成n个不同数的和, 返回所有组合
#define DeletNeiborSameData SetCombina::deletNeiborSameData//从vector删除所有相邻重复元素
#define SortDeletAllSameData SetCombina::sortDeletAllSameData//把vector排序并删除所有重复元素
#define DeletAllSameData SetCombina::deletAllSameData//删除所有重复元素,剩下元素按照首次出现顺序排序
#define GetSameData SetCombina::getSameData//取2个vector的最简交集
#define GetInAnyData SetCombina::getInAnyData//取2个vector的最简并集
#define GetAllSubsets SetCombina::getAllSubsets //求所有子集,flag表示是否去掉重复子集
#define DP_CoinCombine CoinCombine::dp//输入硬币集{25,10,5,1},总数50,输出组合方案数49

///(4.2)数据缓存///
// GetSingleId 略。通用的数据缓存去重编号方案
// GetCombineId 略。对组合型vector<T>数据的优化缓存方案

一,代数

1,代码

class Algebra
{
public:
	//最小距离问题,给出数轴上若干点,求出到任意点的总距离的最小值
	template<typename T>
	static T minSumLen(vector<T>& v)
	{
		sort(v.begin(), v.end());
		T mid = (v[(v.size() - 1) / 2] + v[v.size() / 2]) / 2;
		T ans = 0;
		for (int i = 0; i < v.size(); i++)ans += abs(v[i] - mid);
		return ans;
	}
	//最小距离问题衍生问题,给出数轴上若干点,求出[low,high]段所有整点到所有点的总距离
	template<typename T>
	static vector<T> everSumLen(vector<T> v, int low, int high)
	{
		sort(v.begin(), v.end());
		vector<T>ans;
		int x = low, id = 0;
		T s = 0;
		for (auto vi : v)s += abs(vi - x), id += (vi < x);
		ans.push_back(s);
		for (x = low + 1; x <= high; x++) {
			s += id * 2 - int(v.size());
			while (id<v.size() && x>v[id]) {
				s += 1 + abs(v[id] - x) - abs(v[id] - x + 1);
				id++;
			}
			ans.push_back(s);
		}
		return ans;
	}
};

//闭区间,type=0表示整点区间,type=1表示整段区间,type=2表示浮点数区间
template<int type, typename T = int> //type=2 T=float   type<2 T=int
struct CloseIval
{
	T low, high;
	CloseIval() {}
	CloseIval(T low, T high) :low{ low }, high{ high }
	{
		gap = (type ? 0 : 1);
	}
	bool operator<(const CloseIval& ci)const //按照左端点进行排序
	{
		return low < ci.low;
	}
	bool canMerge(const CloseIval& ci) const
	{
		return ci.low <= high + gap && ci.high >= low - gap;
	}
	void merge(const CloseIval& ci)  //canMerge==true才调用merge
	{
		low = min(low, ci.low), high = max(high, ci.high);
	}
	bool canDel(const CloseIval& ci) const //对于浮点数区间,减法定义不严谨,慎用
	{
		return canMerge(ci);
	}
	vector<CloseIval> del(const CloseIval& ci)const  //canDel==true才调用del
	{
		vector<CloseIval> ans;
		if (low < ci.low)ans.push_back({ low,ci.low - gap });
		if (high > ci.high)ans.push_back({ ci.high + gap,high });
		return ans;//0-2个
	}
private:
	T gap;
};
//闭区间的有序集合,type=0表示整点区间,type=1表示整段区间,type=2表示浮点数区间
template<int type, typename T = int> //type=2 T=float   type<2 T=int
class CloseInterval {
public:
	using Ival = CloseIval<type, T>;
	vector<Ival>allCi;//所有区间,一直保持排序
public:
	//新增区间,尾部插入效率是O(log n),中间插入效率是O(n)
	void push(Ival ci)
	{
  		auto it = lower_bound(allCi.begin(), allCi.end(), ci);
		auto it2 = it;
		bool canMergeFlag = it!= allCi.end() && it->canMerge(ci);
		bool canMergeFlag2 = false;
		if (it != allCi.begin()) {
			it--; //可能可以merge的第一个区间
			canMergeFlag2 = it->canMerge(ci);
		}
		if (!canMergeFlag && !canMergeFlag2) {
			allCi.insert(it2, ci);
			return;
		}
		if (!canMergeFlag2)it = it2;
		//此时it是可以merge的第一个区间
		it->merge(ci);
		it2 = it;
		it2++;
		for (; it2 != allCi.end(); it2++) {
			if (!it->canMerge(*it2))break;
			it->merge(*it2); //往后把所有能merge的都merge到第一个区间
		}
		it++;
		while (it2 != allCi.end())*it++ = *it2++; //剩下没merge的往前平移
		allCi.resize(it - allCi.begin());
	}
	//减掉区间,效率是O(n)
	void del(Ival ci)
	{
		vector<Ival>ans;
		for (auto any : allCi) {
			if (any.canMerge(ci)) {
				auto tmp = any.del(ci);
				for (auto t : tmp)ans.push_back(t);
			}
			else ans.push_back(any);
		}
		allCi = ans;
	}
};

//二进制运算,n>0
class Bits {
public:
	//二进制最低位
	static inline long long getLowBit(long long n) {
		return n & (-n);
	}
	//二进制最高位
	static inline long long getHighBit(long long n) {
		while (n != getLowBit(n)) n ^= getLowBit(n);
		return n;
	}
	//是否是2的幂
	static inline bool isPowerOf2(long long n) {
		return n == getLowBit(n);
	}
	//二进制总位数
	static inline int getBitLength(long long n) {
		int ans = 0;
		while (n)
		{
			n >>= 1;
			ans++;
		}
		return ans;
	}
	//二进制中1的个数
	static inline int getNum1(long long n) {
		int ans = 0;
		while (n)
		{
			n ^= getLowBit(n);
			ans++;
		}
		return ans;
	}
	//二进制中0的个数,只算最高位1后面的0
	static inline int getNum0(long long n) {
		return getBitLength(n) - getNum1(n);
	}
	//二进制反转,01互换,仅限最高位1后面的位
	static inline long long getReverseNum(long long n) {
		long long a = 1, len = getBitLength(n);
		return (a << len) - 1 ^ n;
	}
};
//vector+半群
class VecSemiGroup
{
public:
	template<typename T>
	static vector<T> allExceptOneMin(const vector<T>& v)
	{
		return allExceptOne(v, [](T a, T b) {return min(a, b); });
	}
	template<typename T>
	static vector<T> allExceptOneOr(const vector<T>& v)
	{
		return allExceptOne(v, [](T a, T b) {return a | b; });
	}
protected:
	//枚举只去掉1个数(v.size()>1),剩下的数做p累积运算的结果
	template<typename T, typename Tfunc>
	static vector<T> allExceptOne(const vector<T>& v, Tfunc p) {
		vector<T>left(v.size() - 1), right(v.size() - 1);
		T x = v[0];
		for (int i = 1; i < v.size(); i++)left[i - 1] = x, x = p(x, v[i]);
		x = v.back();
		for (int i = int(v.size()) - 2; i >= 0; i--)right[i] = x, x = p(v[i], x);
		vector<T>ans(1, right[0]);
		for (int i = 0; i < left.size() - 1; i++)ans.push_back(p(left[i], right[i + 1]));
		ans.push_back(left.back());
		return ans;
	}
};

2,测试代码


template<typename T>
static bool isSame(const vector<T>& v1, const vector<T>& v2)
{
	if (v1.size() - v2.size())return false;
	for (int i = 0; i < v1.size(); i++)if (v1[i] != v2[i])return false;
	return true;
}
#define EXPECT_VEC_EQ(a,b) if(!isSame((a),(b))){cout<<"ERROR!!!!!!!!!\n";return false;}
#define EXPECT_EQ(a,b) if(a!=b){cout<<"ERROR!!!!!!!!!\n";return false;}

bool testAlgebra()//待完善
{
	vector<int>v{ 1,3,6 };
	auto ans = EverSumLen(v, 0, 5);//{10,7,6,5,6,7}
	vector<double>v2{ 1, 3, 3.3, 6 };
	auto ans2 = EverSumLen(v2, 1, 4);//{9.3, 7.3, 5.3, 6.7}
	return true;
}

bool testCloseInterval()//待完善
{
	CloseIval<true>{1, 2};
	CloseInterval<true>{};
	return true;
}

bool testBits()//待完善
{
	return true;
}
bool testSemiGroup()//待完善
{
	return true;
}

int main()
{
	if (testAlgebra() && testCloseInterval()&& testBits && testSemiGroup())cout << "test succ!";
	return 0;
}

二,单例、数论

1,代码


template<typename T>
class SingleA {
public:
	static T& GetSingleA() {
		static T s;
		return s;
	}
protected:
	SingleA(const SingleA&) = delete;
	SingleA(SingleA&&) = delete;
	SingleA& operator=(const SingleA&) = delete;
	SingleA& operator=(SingleA&&) = delete;
	SingleA() = default;
	~SingleA() = default;
};
class NumberTheory
{
public:
	//一个数在d进制下的位数
	static int numInRadix(long long m, int d)
	{
		if (m == 0)return 1;
		int s = 0;
		while (m)
		{
			s++;
			m /= d;
		}
		return s;
	}
	//把整数转化为字符串, 出参str不用初始化,返回值是字符串长度
	static int intToStr(long long num, unsigned radix, char*& str)
	{
		int len = numInRadix(num, radix);
		str = new char[len + 2];
		char index[] = "0123456789ABCDEF";
		int k = 0;
		if (num < 0)str[0] = '-', k = 1;
		num = abs(num);
		for (int i = len - 1 + k; i >= k; i--)
		{
			str[i] = index[num % radix], num /= radix;
		}
		str[len + k] = '\0';
		return len + k;
	}
	//把字符串转化为整数
	static long long strToInt(const char* nptr, int radix) //库函数atoi只支持十进制
	{
		int k = 0;
		if (*nptr == '-')k = 1, nptr++;
		long long ans = 0;
		while (*nptr) {
			int x = (*nptr >= '0' && *nptr <= '9') ? *nptr - '0' : *nptr - 'A';
			ans = ans * radix + x, nptr++;
		}
		return k ? -ans : ans;
	}
	// 素数检测
	static bool isPrime(int n)
	{
		if (n == 2)return true;
		if (n % 2 == 0)return false;
		for (int i = 3; i * i <= n; i += 2) if (n % i == 0)return false;
		return true;
	}
	//把3245分解成3*1000+245,出参a=3 b=1000 c=245
	static void getHighNum(int n, int& a, int& b, int& c)
	{
		int m = n;
		b = 1;
		m /= 10;
		while (m)b *= 10, m /= 10;
		a = n / b, c = n % b;
	}
	//一个十进制数里面有没有数字9
	static bool isHas9(int n)
	{
		while (n) {
			if (n % 10 == 9)return true;
			n /= 10;
		}
		return false;
	}
	//1-n中有多少十进制数里面有数字9,n<10^9
	static int numHas9(int n)
	{
		if (n <= 1)return 0;
		int num[10] = { 0,1,19,271,3439,40951,468559,5217031,56953279,612579511 };
		int a, b, c;
		getHighNum(n, a, b, c);
		int d = 0;
		while (b > 1)d++, b /= 10;
		return a * num[d] + (a == 9 ? c + 1 : isHas9(c));
	}
	//是否为回文数
	static bool isPalindrome(int x)
	{
		if (x < 0)return false;
		vector<int>num;
		while (x)
		{
			num.insert(num.end(), x % 10);
			x /= 10;
		}
		int len = num.size();
		for (int i = 0, j = len - 1; i < j; i++, j--)
		{
			if (num[i] - num[j])return false;
		}
		return true;
	}
	//最大公约数
	static long long gcd(long long a, long long b)
	{
		if (b == 0)return a;
		return gcd(b, a % b);
	}
	//最小公倍数
	static long long lcm(long long a, long long b)
	{
		a /= gcd(a, b);
		if (a > LLONG_MAX / b)return 0;
		return a * b;
	}
};
class Sieve : public SingleA<Sieve>
{
	static const int N = 200000;
	friend class SingleA<Sieve>;
public:
	bool isPrime(int n)//判断n是不是素数
	{
		if (n < 2)return false;
		calPrime(n);
		return prime[n];
	}
	vector<int> getPrime(int n)//获取[1,n]内的所有素数,如果曾经传过更大的N,则返回[1,N]内的所有素数
	{
		if (n < 2)return vector<int>{};
		calPrime(n);
		return vPrime;
	}
private:
	Sieve() {
		prime[0] = prime[1] = 0, prime[2] = 1;
		flag = 2;
		for (int i = 3; i < N; i += 2)prime[i] = 1;
		vPrime.reserve(N / 5);
		vPrime.push_back(2);
	}
	void calPrime(int n)//判断[1,n]内的素数,n<N
	{
		if (flag >= n)return;
		for (int i = flag + 1 + flag % 2; i <= n; i += 2)if (prime[i])
		{
			vPrime.push_back(i);
			for (int j = i + i; j < N; j += i)prime[j] = 0;
		}
		flag = n;
	}
private:
	int flag;
	int prime[N];
	vector<int>vPrime;
};
class Facs:public Sieve
{
public:
	//因式分解,把12分解成{<2,2>,<3,1>}
	static vector<pair<int, int>> fenjie(int n)
	{
		vector<pair<int, int>> vp;
		for (auto i : GetSingleA().getPrime(sqrt(n + 1)))
		{
			if (n % i)continue;
			int k = 0;
			while (n % i == 0)n /= i, k++;
			vp.push_back({ i, k });
		}
		if (n > 1) vp.push_back({ n, 1 });
		return vp;
	}
	//积性分解,即单素数幂分解,把12分解成{4,3}
	static vector<int> fenjie2(int n)
	{
		vector<pair<int, int>> vp = fenjie(n);
		vector<int>ans;
		for (auto vi : vp) {
			int s = 1;
			while (vi.second--)s *= vi.first;
			ans.push_back(s);
		}
		return ans;
	}
	//获取所有因子
	static vector<int> getFacs(int n)
	{
		vector<pair<int, int>> vp = fenjie(n);
		vector<int>ans;
		for (auto vi : vp)ans.push_back(vi.first);
		return ans;
	}
	//获取最大因子
	static int getMaxFacs(int n)
	{
		vector<int> v = getFacs(n);
		return *v.rbegin();
	}
	//获取所有约数
	static vector<int> getDivisors(int n)
	{
		vector<pair<int, int>>v = fenjie(n);
		vector<int>ans, ans2;
		ans.push_back(1);
		if (n <= 1)return ans;
		for (auto vi : v) {
			vector<int>ans2(ans.size() * (vi.second + 1));
			copy(ans.begin(), ans.end(), ans2.begin());
			for (int i = ans.size(); i < ans2.size(); i++) ans2[i] = ans2[i - ans.size()] * vi.first;
			ans = ans2;
		}
		sort(ans.begin(), ans.end());
		return ans;
	}
    //在x的所有约数中,寻找离a最近的一个
	static int findNear(int x, int a) // 1<a<x
	{
		auto v = getDivisors(x);
		for (int i = 0; i < v.size(); i++) {
			if (v[i + 1] <= a)continue;
			if (v[i] + v[i + 1] - a * 2 > 0)return v[i];
			return v[i + 1];
		}
		return 0;
	}
	//判断是不是平方和
	static bool isSquareSum(int n)
	{
		vector<pair<int, int>> vp = fenjie(n);
		for (auto &p : vp) {
			if (p.first % 4 == 3 && p.second % 2 == 1)return false;
		}
		return true;
	}
};
class Phi :public Facs
{
public:
	//欧拉函数
	static int getPhi(int n)
	{
		static map<int, int>m;
		if (m[n])return m[n];
		return m[n] = getPhiWithFacs(n, getFacs(n));
	}
	//计算phi(n)的所有因子facs,返回phi(n)
	static int getFacsOfPhi(int n, vector<int>& facs)
	{
		vector<pair<int, int>> vp = fenjie(n);
		vector<int>v;
		set<int>facSet;
		for (auto vi : vp) {
			v.push_back(vi.first);
			if (vi.second > 1)facSet.insert(vi.first);
			for (auto x : getFacs(vi.first - 1))facSet.insert(x);
		}
		for (auto x : facSet)facs.push_back(x);
		return getPhiWithFacs(n, v);
	}
private:
	//已知n的所有因子,求欧拉函数
	static int getPhiWithFacs(int n, const vector<int>& v)
	{
		int ans = n;
		for (auto vi : v)ans = ans / vi * (vi - 1);
		return ans;
	}
};
class Order :public Phi, public NumberTheory
{
public:
	//求a mod n的阶,-1表示不存在
	static int getOrder(int a, int n)
	{
		if (gcd(a, n) != 1)return -1;
		vector<int> facsOfPhi;
		int thephi = getFacsOfPhi(n, facsOfPhi);
		return getOrder(a, n, thephi, facsOfPhi);
	}
private:
	//求a mod n的阶,一定是upPie的约数
	static int getOrder(int a, int n, int upPie, vector<int>& facsOfPhi)
	{
		for (auto vi : facsOfPhi) {
			while (upPie % vi == 0 && aijiMulti(a, upPie / vi, n) % n == 1)upPie /= vi;
		}
		return upPie;
	}
	template<typename A, typename N>
	static inline A aijiMulti(A a, N n, int p = INT_MAX)
	{
		if (n <= 1)return a;
		A ans = aijiMulti<A, N>(a, n / 2, p, pfunc);
		ans = (ans * ans) % p;
		if (n % 2)ans = (ans * a) % p;
		return ans;
	}
};

class Factorial:public Facs
{
public:
	static inline int degree(int m, int p)//求m!中素数p的次数
	{
		int ans = 0;
		while (m)ans += m / p, m /= p;
		return ans;
	}
	static inline int degree2(int m, int n)//求m!中n的次数
	{
		vector<pair<int, int>> vp = fenjie(n);
		int ans = degree(m, vp[0].first) / vp[0].second;
		for (int i = 1; i < vp.size(); i++)ans = min(ans, degree(m, vp[i].first) / vp[i].second);
		return ans;
	}
	static vector<pair<int, int>> factorization(int n) //求n!的因式分解
	{
		vector<pair<int, int>> vp;
		for (auto i : GetSingleA().getPrime(n)) {
			if (i > n)break;
			vp.push_back({ i, degree(n,i) });
		}
		return vp;
	}
};

2,测试代码


template<typename T>
static bool isSame(const vector<T>& v1, const vector<T>& v2)
{
	if (v1.size() - v2.size())return false;
	for (int i = 0; i < v1.size(); i++)if (v1[i] != v2[i])return false;
	return true;
}
#define EXPECT_VEC_EQ(a,b) if(!isSame((a),(b))){cout<<"ERROR!!!!!!!!!\n";return false;}
#define EXPECT_EQ(a,b) if(a!=b){cout<<"ERROR!!!!!!!!!\n";return false;}

bool testSingle()//待完善
{
	return true;
}

bool testNumberTheory()//待完善
{
	NumberTheory{};
	return true;
}
bool testSieve()//待完善
{
	Sieve::GetSingleA();
	return true;
}
bool testFacs()//待完善
{
	Facs::fenjie(100);
	return true;
}
bool testPhi()//待完善
{
	Phi::getPhi(100);
	return true;
}
bool testOrder()//待完善
{
	Order::getOrder(10, 100);
	return true;
}

bool testFactorial()//待完善
{
	return true;
}


int main()
{
	if (testSingle() && testNumberTheory() && testSieve() && testFacs() && testPhi() && testOrder() && testFactorial())cout << "test succ!";
	return 0;
}

三,快速幂、BSGS、逆元、组合数、全排列、容斥原理

1,代码

class Multi
{
public:
	template<typename N>
	static long long multiAdd(long long a, N n, int p = INT_MAX)//快速乘法,n>0
	{
		return aijiMulti(a, n, p, opAdd<long long>);
	}
	template<typename N>
	static long long multiMulti(long long a, N n, int p = INT_MAX)//快速幂,n>0
	{
		return aijiMulti(a, n, p, opMulti<long long>);
	}
	template<typename N>
	static long long multiMultiAdd(long long a, N n, int p = INT_MAX)//快速幂套快速乘法,n>0
	{
		return aijiMulti(a, n, p, opMultiAdd<long long>);
	}
	template<typename A, typename N>
	static vector<vector<A>> multiMatrix(vector<vector<A>> a, N n, int p = INT_MAX)//矩阵快速幂,n>0
	{
		return aijiMulti(a, n, p, opMatrixMulti<A>);
	}
protected:
	template<typename A>
	static inline A opAdd(A x, A y, int p)
	{
		return (x + y) % p;
	}
	template<typename A>
	static inline A opMulti(A x, A y, int p)
	{
		return (x * y) % p;
	}
	template<typename A>
	static inline A opMultiAdd(A x, A y, int p)
	{
		return aijiMulti(x, y, p, opAdd<A>);
	}
	template<typename A>
	static inline vector<vector<A>> opMatrixMulti(vector<vector<A>> x, vector<vector<A>> y, int p)
	{
		vector<vector<A>> ans = x;
		for (int i = 0; i < ans.size(); i++) {
			for (int j = 0; j < ans.size(); j++) {
				ans[i][j] = 0;
				for (int k = 0; k < ans.size(); k++)ans[i][j] += (x[i][k] * y[k][j]) % p;
			}
		}
		return ans;
	}
	template<typename A, typename N>
	static inline A aijiMulti(A a, N n, int p, A(*pfunc)(A, A, int))
	{
		if (n <= 1)return a;
		A ans = aijiMulti<A, N>(a, n / 2, p, pfunc);
		ans = pfunc(ans, ans, p);
		if (n % 2)ans = pfunc(ans, a, p);
		return ans;
	}
};
class BSGS :public Multi //求离散对数a^x=b(mod p)
{
public:
	static long long discreteLog(long long a, long long b, int p, int order) // order是a mod p的阶
	{
		a = (a % p + p) % p, b = (b % p + p) % p;
		if (a == 0)return (b == 0) ? 1 : -1;
		long long m = (long long)sqrt(double(order));
		long long c = multiMulti(a, p - 1 - m, p);
		set<long long>s;
		map<long long, long long>ma;
		long long x = 1;
		for (int j = 0; j < m; j++) {
			if (j > 0 && x == 1)break;
			s.insert(x);
			ma[x] = j;
			x = x * a % p;
		}
		for (int i = 0; i <= order / m; i++) {
			if (s.find(b) != s.end()) {
				return i * m + ma[b];
			}
			b = b * c % p;
		}
		return -1;
	}
};
class InverseNum :public Multi
{
public:
	int getInverseNum(int n, int p) { //p是素数, n不是p的倍数
		if (p == 2)return 1;
		if (this->p != p)m.clear(), this->p = p;
		if (m[n])return m[n];
		return m[n] = multiMulti(n, p - 2, p);
	}
protected:
	int p = 1000000007;
	map<int, int>m;
};
template<int N = 200000>
class ComNum :public InverseNum
{
public:
	ComNum() {
		prime[0] = prime[1] = 0, prime[2] = 1;
	}
	int getComNum(int n, int k, int p = INT_MAX) //组合数C(n,k)%p
	{
		return getComNum(n, vector<int>{n}, vector<int>{k, n - k}, p);
	}
	int getComNum(int maxNum, const vector<int>& v1, const vector<int>& v2, int p = INT_MAX)//v1的阶乘积除以v2的阶乘积,maxNum是所有数中的最大值
	{
		vector<pair<int, int>> v = ComFenjie(maxNum, v1, v2);
		long long ans = 1;
		for (auto par : v)
		{
			int mi = par.second;
			while (mi--)ans = ans * par.first % p;
		}
		return ans;
	}
	int getFactorialNum(int n, int p = INT_MAX) //n! % p
	{
		int a = factorial.size();
		if (n < a)return factorial[n];
		factorial.resize(n + 1);
		for (int i = a; i <= n; i++) {
			factorial[i] = factorial[i - 1] * i % p;
		}
		return factorial[n];
	}
	int getValueModp(const vector<int>& v1, const vector<int>& v2, int p = 1000000007)//v1的阶乘积除以v2的阶乘积mod p,仅限p是素数
	{
		long long ans = 1;
		for (auto x : v1) {
			ans = ans * getFactorialNum(x, p) % p;
		}
		for (auto x : v2) {
			ans = ans * getInverseNum(getFactorialNum(x, p), p) % p;
		}
		return ans;
	}
	static vector<vector<int>> getAllValue(int n, int k, int p = INT_MAX)//所有组合数C(i,j)%p,0<=i<=n,0<=j<=k
	{
		vector<vector<int>>ans(n + 1);
		for (int i = 0; i <= n; i++) {
			ans[i].resize(k + 1);
			ans[i][0] = 1;
			if (i) for (int j = 1; j <= k; j++)ans[i][j] = (ans[i - 1][j] + ans[i - 1][j - 1]) % p;
		}
		return ans;
	}
protected:
	static inline int degree(int m, int p)//求m!中素数p的次数
	{
		int ans = 0;
		while (m)ans += m / p, m /= p;
		return ans;
	}
	vector<pair<int, int>> ComFenjie(int maxNum, const vector<int>& v1, const vector<int>& v2)//v1的阶乘积除以v2的阶乘积的因式分解
	{
		auto ps = getPrime(maxNum + 1);
		vector<pair<int, int>> ans;
		for (auto p : ps) {
			int mi = 0;
			for (auto x : v1)mi += degree(x, p);
			for (auto x : v2)mi -= degree(x, p);
			if (mi > 0)ans.push_back(make_pair(p, mi)); // 输入保证结果是整数
		}
		return ans;
	}
	vector<int> getPrime(int n)//获取[1,n]内的所有素数,如果曾经传过更大的N,则返回[1,N]内的所有素数
	{
		if (n < 2)return vector<int>{};
		calPrime(n);
		return vPrime;
	}
	void calPrime(int n)//判断[1,n]内的素数,n<N
	{
		if (flag == 0) {
			flag = 2;
			for (int i = 3; i < N; i += 2)prime[i] = 1;
			vPrime.reserve(N / 5);
			vPrime.push_back(2);
		}
		if (flag >= n)return;
		for (int i = flag + 1 + flag % 2; i <= n; i += 2)if (prime[i])
		{
			vPrime.push_back(i);
			for (int j = i + i; j < N; j += i)prime[j] = 0;
		}
		flag = n;
	}
private:
	vector<long long>factorial{ 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
	int flag = 0;
	int prime[N];
	vector<int>vPrime;
};
class Permutations :public ComNum<>
{
public:
	Permutations() {}
	int getPermutationNum(int n, int p = INT_MAX) //n个不同元素的全排列
	{
		return getFactorialNum(n, p);
	}
	int getPermutationNum(const vector<int>& v, int p = INT_MAX)//输入每个元素的计数,输出全排列
	{
		int s = 0;
		for (auto x : v)s += x;
		return getValueModp(vector<int>{s}, v, p); //全排列公式(a+b+c)!/a!/b!/c!
	}
};
//容斥原理
template<typename T = long long>
class InclusionExclusion {
public:
	T getSum(int low, int high) {
		T ans = 0, flag = 1;
		for (int i = low; i <= high; i++) {
			ans += getValue(i) * flag;
			flag *= -1;
		}
		return ans;
	}
protected:
	virtual T getValue(int key) {
		return 0;
	}
};
//在n个球中可重复的选s次,求其中b个球至少被选中一次的情况数
class IECombina :public InclusionExclusion<>, public ComNum<> {
public:
	long long getSumNum(int n, int s, int b) {
		this->n = n, this->s = s, this->b = b;
		return getSum(0, b);
	}
private:
	long long getValue(int key) {
		return Multi::multiMulti(n - key, s, p) * getComNum(b, key, p) % p;
	}
	int n, s, b;
	vector<long long>comninaNum;
	int p = 1000000007;
};
//在n个球中可重复的选s次,每相邻k次之中不能有重复的球,求其中每个球至少被选中一次的情况数
class IECombina2 :public InclusionExclusion<>, public ComNum<> {
public:
	long long getSumNum(int n, int s, int k) {
		this->n = n, this->s = s, this->k = k;
		long long ans = (getSum(0, n - k) % p + p) % p;
		if (n % 2 != k % 2)ans = p - ans;
		for (int i = 0; i < k; i++)ans = ans * (n - i) % p;
		return ans % p;
	}
private:
	long long getValue(int key) {
		return Multi::multiMulti(key, s - k, p) * getComNum(n - k, key, p) % p;
	}
	int n, s, k;
	int p = 1000000007;
};
class IECombina3 :public InclusionExclusion<> {
public:
	long long getSumNum(const vector<int>& v, long long n) { //从1到n有多少数是v中任一数的倍数
		m.clear();
		for (int i = 1; i < (1 << v.size()); i++) {
			long long lcms = 1;
			int s = 0;
			for (int j = 0; j < v.size(); j++) {
				if (i & (1 << j))s++, lcms = lcm(lcms, v[j]);
				if (lcms > n || lcms == 0)break;
			}
			m[s] += n / lcms;
		}
		return getSum(1, v.size());
	}
private:
	long long getValue(int key) {
		return m[key];
	}
	//最大公约数
	static long long gcd(long long a, long long b)
	{
		if (b == 0)return a;
		return gcd(b, a % b);
	}
	//最小公倍数
	static long long lcm(long long a, long long b)
	{
		a /= gcd(a, b);
		if (a > LLONG_MAX / b)return 0;
		return a * b;
	}
private:
	map<int, long long>m;
};

2,测试代码


#define EXPECT_EQ(a,b) if(a!=b){cout<<"ERROR!!!!!!!!!\n";return false;}

bool testMulti()//待完善
{
	EXPECT_EQ(MultiMulti(2, 10), 1024);
	return true;
}
bool testBSGS()//待完善
{
	int order = 96;
	BSGS::discreteLog(2, 1, 97, order);
	return true;
}
bool testInverseNum()//待完善
{
	return true;
}
bool testComNum()//待完善
{
	return true;
}
bool testPermutations()//待完善
{
	return true;
}
bool testIEs()//待完善
{
	return true;
}
int main()
{
	if (testMulti() && testBSGS() && testInverseNum() && testComNum() && testPermutations() && testIEs())cout << "test succ!";
	return 0;
}

四,排列组合

1,代码

class Combina
{
public:
	//在一个列表中选出若干个数,得到所有排列,按照id的字典序来排序
	template<typename T>
	static vector<vector<T>> getAllPermutation(const vector<T>& v, int n, bool flag)// flag表示是否去重
	{
		vector<vector<T>>ans;
		if (n <= 0 || n > v.size())return ans;
		vector<T>tmp(n);
		vector<int>m(v.size() + 1, 0);
		dfs(n, 1, 0, v, m, tmp, ans, flag, true);
		return ans;
	}
	//在一个列表中选出若干个数,得到所有组合,按照id的字典序来排序
	template<typename T>
	static vector<vector<T>> getAllCombina(const vector<T>& v, int n, bool flag)// flag表示是否去重
	{
		vector<vector<T>>ans;
		if (n <= 0 || n > v.size())return ans;
		vector<T>tmp(n);
		vector<int>m(v.size() + 1, 0);
		dfs(n, 1, 0, v, m, tmp, ans, flag, false);
		return ans;
	}
	//把s拆分成n个不同数的和, 返回所有排列
	static vector<vector<int>> intSplitA(int s, int n, const set<int>& m)//m给出了可选的所有数
	{
		vector<vector<int>>ans;
		if (n == 1) {
			if (m.find(s) == m.end())return ans;
			auto x = vector<vector<int>>(1, vector<int>(1, s));
			return x;
		}
		for (auto p : m) {
			set<int>m2 = m;
			m2.erase(p);
			vector<vector<int>>v = intSplitA(s - p, n - 1, m2);
			for (auto& vi : v) {
				vi.push_back(p);
				ans.push_back(vi);
			}
		}
		return ans;
	}
	//把s拆分成n个不同数的和, 返回所有组合
	static vector<vector<int>> intSplitC(int s, int n, set<int>& m)//m给出了可选的所有数
	{
		vector<vector<int>>ans;
		if (m.empty())return ans;
		if (n == 1) {
			if (m.find(s) == m.end())return ans;
			auto x = vector<vector<int>>(1, vector<int>(1, s));
			return x;
		}
		int p = *m.begin();
		m.erase(p);
		set<int> m2 = m;
		ans = intSplitC(s, n, m);
		vector<vector<int>>v = intSplitC(s - p, n - 1, m2);
		for (auto& vi : v) {
			vi.push_back(p);
			ans.push_back(vi);
		}
		return ans;
	}
private:
	template<typename T>
	static void dfs(int n, int deep, int id, const vector<T>& v, vector<int>&m, vector<T>& tmp, vector<vector<T>>& ans, bool flag, bool isPermutation)
	{
		if (deep > n) {
			ans.push_back(tmp);
			return;
		}
		if (id >= v.size())return;
		map<T, int>m2;
		for (int i = (isPermutation ? 0 : id); i < v.size(); i++) {
			if (m[i] || (flag && m2[v[i]]))continue;
			tmp[deep - 1] = v[i];
			m[i] = 1, m2[v[i]] = 1;
			dfs(n, deep + 1, i + 1, v, m, tmp, ans, flag, isPermutation);
			m[i] = 0;
		}
	}
};

class SetCombina {
public:
	//从vector删除所有相邻重复元素
	template<typename T>
	static void deletNeiborSameData(vector<T>& v)
	{
		for (int i = 1; i < v.size(); i++)if (v[i] == v[i - 1])v.erase(v.begin() + i--);
	}
	//把vector排序并删除所有重复元素
	template<typename T>
	static void sortDeletAllSameData(vector<T>& v)
	{
		sort(v.begin(), v.end());
		deletNeiborSameData(v);
	}
	//删除所有重复元素,剩下元素按照首次出现顺序排序
	template<typename T>
	static void deletAllSameData(vector<T>& v)
	{
		for (int i = 1; i < v.size(); i++) {
			bool flag = true;
			for (int j = 0; flag && j < i; j++)if (v[i] == v[j])flag = false;
			if (!flag)v.erase(v.begin() + i--);
		}
	}
	//取2个vector的最简交集
	template<typename T>
	static vector<T> getSameData(vector<T>& v1, vector<T>& v2)
	{
		sortDeletAllSameData(v1);
		sortDeletAllSameData(v2);
		vector<T>ans;
		for (int i = 0, j = 0; i < v1.size() && j < v2.size();) {
			if (v1[i] < v2[j])i++;
			else if (v1[i] > v2[j])j++;
			else ans.push_back(v1[i++]);
		}
		return ans;
	}
	//取2个vector的最简并集
	template<typename T>
	static vector<T> getInAnyData(const vector<T>& v1, const vector<T>& v2)
	{
		vector<T>ans(v1.size() + v2.size());
		copy(v1.begin(), v1.end(), ans.begin());
		copy(v2.begin(), v2.end(), ans.begin() + v1.size());
		sortDeletAllSameData(ans);
		return ans;
	}
	//求所有子集,flag表示是否去掉重复子集
	static vector<vector<int>> getAllSubsets(vector<int>& nums, bool flag) { 
		if(flag)sort(nums.begin(), nums.end());
		int s = (1 << nums.size());
		vector<vector<int>>ans;
		for (int k = 0; k < s; k++) {
			vector<int>v;
			for (int i = 0; i < nums.size(); i++)if (k&(1 << i))v.push_back(nums[i]);
			ans.push_back(v);
		}
		if (flag)sortDeletAllSameData(ans);
		return ans;
	}
};
class CoinCombine
{
public:
	//输入硬币集{25,10,5,1},总数50,输出组合方案数49
	static long long dp(vector<int>coins, int s, int p = 1000000007)
	{
		sort(coins.begin(), coins.end());
		return dp(coins, s, coins.size() - 1, p);
	}
private:
	static long long dp(const vector<int>&coins, int s, int id, int p)
	{
		static map<int, map<int, long long>>m;
		if (s <= 0 || id < 0)return (s == 0);
		if (m[s][id])return m[s][id];
		return m[s][id] = (dp(coins, s - coins[id], id, p) + dp(coins, s, id - 1, p)) % p;
	}
};

//通用的数据缓存去重编号方案
template<typename T>
class GetSingleId
{
public:
	int id(T x) //所有元素按照首次传入顺序编号0,1,2......
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		mr[n] = x;
		return m[x] = n++;
	}
	int num() //id()==num()-1表示是新元素,否则是重复元素
	{
		return n;
	}
	T getData(int id) //根据id获取元素数据
	{
		return mr[id];
	}
private:
	map<T, int>m;
	map<int, T>mr;
	int n = 0;
};
//对组合型vector<T>数据的优化缓存方案
template<typename T>
class GetCombineId
{
public:
	int id(vector<T>& x)
	{
		return v2.id(combineId(x));
	}
	int num()
	{
		return v2.num();
	}
	vector<T> getData(int id)
	{
		vector<int>ids = v2.getData(id);
		vector<T>ans(v.size());
		for (int i = 0; i < v.size(); i++)ans[i] = v[i].getData(ids[i]);
		return ans;
	}
private:
	vector<int> combineId(vector<T>& x)
	{
		if (v.empty())v.resize(x.size());
		vector<int>ans(x.size());
		for (int i = 0; i < x.size(); i++)ans[i] = v[i].id(x[i]);
		return ans;
	}
private:
	vector<GetSingleId<T>>v;
	GetSingleId<vector<int>>v2;
};

2,测试代码


template<typename T>
static bool isSame(const vector<T>& v1, const vector<T>& v2)
{
	if (v1.size() - v2.size())return false;
	for (int i = 0; i < v1.size(); i++)if (v1[i] != v2[i])return false;
	return true;
}
#define EXPECT_VEC_EQ(a,b) if(!isSame((a),(b))){cout<<"ERROR!!!!!!!!!\n";return false;}
#define EXPECT_EQ(a,b) if(a!=b){cout<<"ERROR!!!!!!!!!\n";return false;}


bool testCombina()//待完善
{
	Combina{};
	return true;
}
bool testSetCombina()
{
	vector<int>v1{ 1, 2,2,3,3, 5 };
	DeletNeiborSameData(v1);
	EXPECT_VEC_EQ(v1, (vector<int>{1, 2, 3, 5}));
	v1.push_back(2);
	EXPECT_VEC_EQ(v1, (vector<int>{1, 2, 3, 5, 2}));
	SortDeletAllSameData(v1);
	EXPECT_VEC_EQ(v1, (vector<int>{1, 2, 3, 5}));
	v1.push_back(2);
	v1.push_back(4);
	EXPECT_VEC_EQ(v1, (vector<int>{1, 2, 3, 5, 2, 4}));
	DeletAllSameData(v1);
	EXPECT_VEC_EQ(v1, (vector<int>{1, 2, 3, 5, 4}));
	vector<int>v2{ 3,6 };
	EXPECT_VEC_EQ(GetSameData(v1, v2), (vector<int>{3}));
	EXPECT_VEC_EQ(GetInAnyData(v1, v2), (vector<int>{1, 2, 3, 4, 5, 6}));
	return true;
}
bool testCoinCombine()//待完善
{
	return true;
}
bool testGetSingleId()//待完善
{
	GetSingleId<int>{};
	return true;
}
bool testGetCombineId()//待完善
{
	GetCombineId<int>{};
	return true;
}

int main()
{
	if (testCombina() && testSetCombina() && testCoinCombine() && testGetSingleId() && testGetCombineId())cout << "test succ!";
	return 0;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
上海交通大学ACM模板是指为了方便上海交通大学ACM队伍在参加ACM国内外比赛时,准备的一份包含常用算法模板和数据结构实现的文件。这份模板ACM队伍日常训练和比赛中必备的工具和参考资料。 ACM模板通常包括多个文件,每个文件对应一个具体的算法或数据结构,并提供了相应算法的思想、伪代码和具体实现。常见的内容包括但不限于:搜索算法、图论算法、动态规划、字符串处理、数论算法、几何算法、数据结构等。 ACM模板的好处主要有以下几点: 1. 提高编程效率:ACM模板中的算法和数据结构已经经过了优化和测试,可以直接拿来使用,避免了从零开始编写代码的时间和精力消耗。 2. 加深理解:通过研究ACM模板中的算法和数据结构实现,可以更深入地了解算法的原理和应用场景,从而提升对ACM竞赛中常见问题的解决能力。 3. 快速调试:ACM比赛通常时间紧迫,要求快速解决问题。ACM模板可以提供一些已经调试通过的代码,可以直接用于ACM比赛中,减少调试的时间。 4. 统一编程习惯:ACM模板中的代码通常是经过一段时间的磨合和调试得到的,可以作为一个学习的范本,帮助学习者养成良好的编程习惯。 上海交通大学ACM模板是上海交通大学ACM队伍根据自身经验和成果整理而成的,旨在为队员提供便利和帮助。同时,ACM模板也可以随着时间的推移进行更新和完善,以适应新的算法和数据结构的发展和变化。 总的来说,上海交通大学ACM模板ACM竞赛中的宝贵资料,对于提升队伍的竞赛实力和解决问题的效率具有重要意义。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值