11.18补充模板

unordered_map

基本参数

template < class Key,                                    // unordered_map::key_type
           class T,                                      // unordered_map::mapped_type
           class Hash = hash<Key>,                       // unordered_map::hasher
           class Pred = equal_to<Key>,                   // unordered_map::key_equal
           class Alloc = allocator< pair<const Key,T> >  // unordered_map::allocator_type
           > class unordered_map;

预设初始大小

unordered_map<int,int>mp(10000);\\预设大小为1e4的umap

hash函数(防止卡hash)

const int p1 = 998244353, p2 = 1e9 + 7, p3 = 1e9 + 9;
struct hashh{
    size_t operator()(const int& xx)const
    {
        return (xx % p1) * (xx % p2) % p3;
    }
};
unordered_map<int, int, hashh> mp;

Lucas定理

\(\dbinom{m}{n} \%p =\dbinom{m/p}{n/p} \dbinom{m\%p}{n\%p} \%p (p为质数)\)

证明:

由二项式定理展开可得结论一:\((1+x)^p = C_p^0x^0 + C_p^1x^1 + C_p^2x^2+…C_p^px^p=1+x^p (mod\quad p)\)
\(m=ap+c,n=bp+d\),则\((1+x)^n = (1+x)^{bp+d}=(1+x^p)^b (1+x)^d (mod \quad p)\)
此时\(\dbinom{m}{n}x^m= \dbinom{a}{b} x^{ap} * \dbinom{c}{d} x^c\)
且当c>d时显然有\(\dbinom{c}{d}=\dbinom{m}{n}=0\)
QED.

扩展Lucas定理

一个相对简单的情况

当p为非质数时,\(C_p^i(1 <= i <= p-1) \equiv 0(mod\ p)\)无法保证,p因子可能会和p进行约分。
譬如\(\dbinom{4}{6}\%6=3≠0\)
当p可以被分解为几个质数均为1的质数时可以对质数使用lucas定理,最后再用crt合并即可。

更一般的情况

根据唯一分解定理,得到\(p=\prod \limits p^{k_i}_i\)
考虑在\(\mod p^{k_i}_i\)意义下的lucas定理,最后结果仍可以用crt合并。
大致方法是在\(\mod p^{k_i}_i\)意义下快速求阶乘,再合并阶乘结果为组合数:
首先我们先提取出所有p的倍数,对于n,其阶乘内部有\(\lfloor\frac{n}{p}\rfloor\)个p的倍数,把他们全部提取出来,结果就是\(p^{\lfloor\frac{n}{p}\rfloor} * \lfloor\frac{n}{p}\rfloor!\),其中 \(\lfloor\frac{n}{p}\rfloor!\)可以递归计算。
对于不是p的倍数的,每个\(p^k\)成一个循环节,在每个循环节里面是直接把乘积算出来,最后再套上\(⌊\frac{n}{p^k}⌋\)的指数。最后会剩余几项,那些直接暴力乘起来就行。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100;
const double esp=1e-8;
ll w[10];
ll n,m,P;
ll sum;
template<typename T>
inline void read(T &x){
	x=0;T fl=1;char tmp=getchar();
	while(tmp<'0'||tmp>'9')fl=tmp=='-'?-fl:fl,tmp=getchar();
	while(tmp>='0'&&tmp<='9')x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
	x=x*fl;
}
ll mul(ll a,ll b,ll t){
	ll res=a*b-(ll)((long double)a/t*b+esp)*t;
	return (res%t+t)%t;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
	if(!b){
		x=1,y=0;return a;
	}
	ll d=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return d;
}
ll inv(ll a,ll b){
	ll x,y;
	exgcd(a,b,x,y);
	return (x%b+b)%b;
}
ll crt(ll b,ll t){return mul(mul(b,inv(P/t,t),P),P/t,P);}
ll qpow(ll a,ll b,ll t){
	ll P=1;
	while(b){
		if(b&1)P=mul(P,a,t);
		a=mul(a,a,t),b>>=1;
	}
	return P;
}
ll fac(ll n,ll pi,ll pk){
	if(!n) return 1;
	ll res=1;
	for(int i=2;i<=pk;i++)if(i%pi)res*=i,res%=pk;
	res=qpow(res,n/pk,pk);
	for(int i=2;i<=n%pk;i++)if(i%pi)res*=i,res%=pk;
	return res*fac(n/pi,pi,pk)%pk;
}
ll C(ll n,ll m,ll pi,ll pk){
	ll d=fac(n,pi,pk),d1=fac(m,pi,pk),d2=fac(n-m,pi,pk);
	ll k=0;
	for(ll i=n;i;i/=pi)k+=i/pi;
	for(ll i=m;i;i/=pi)k-=i/pi;
	for(ll i=n-m;i;i/=pi)k-=i/pi;
	return mul(mul(d,inv(d1,pk),pk),mul(qpow(pi,k,pk),inv(d2,pk),pk),pk);
}
ll exlucas(ll n,ll m){
	ll res=0,tmp=P,pk;
	for(ll i=2;i*i<=tmp;i++){
		pk=1;
		while(!(tmp%i))tmp/=i,pk*=i;
		res+=crt(C(n,m,i,pk),pk),res%=P;
	}
	if(tmp>1)
	res+=crt(C(n,m,tmp,tmp),tmp),res%=P;
	return res;
}
int main(){
	cin>>P>>n>>m;
	for(int i=1;i<=m;i++)
		read(w[i]),sum+=w[i];
	if(sum>n)
		printf("Impossible\n");
	else{
		ll ans=1;
		for(int i=1;i<=m;i++)
			ans*=exlucas(n,w[i]),ans%=P,n-=w[i];
		printf("%lld\n",ans);
	}
	return 0;
}

中国剩余定理crt

O(n)

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
using namespace std;

const double eps = 1e-10;
const double pi = acos(-1.0);

int n;
ll a[30],m[30],M = 1;
/*
算法步骤:
计算模数积,然后遍历每个方程,让答案累加a[i]*M/m[i]*inv(M/m[i],m[i]);
其中inv(a,b)表示a在modb意义下的逆元。
注意,中国剩余定理必须要满足模数两两互质。
*/

template<class T> T gcd(T a, T b){
	return !b?a:gcd(b,a%b);
}

void exgcd(ll a, ll b, ll& x, ll& y){
	if(!b){
		x = 1, y = 0;
		return;
	}
	exgcd(b,a%b,y,x);
	y -= a/b*x;
}

ll inv(ll a, ll b){
	ll x,y;
	exgcd(a,b,x,y);
	return (x%b+b)%b;
}

ll solve(){
	ll ans = 0;
	for(int i = 1; i <= n; i++){
		ans = (ans + a[i]*M/m[i]*inv(M/m[i],m[i]))%M;
	}
	return (ans%M+M)%M; 
}

int main()
{
    scanf("%d",&n);
	for(int i = 1; i <= n; i++){
		scanf("%lld%lld",&m[i],&a[i]);
		M *= m[i];
	}
	printf("%lld\n",solve());
	return 0;
}

扩展中国剩余定理ex_crt

O(nlogn)
本质解同余方程组,转化为求n个同余方程

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
using namespace std;

const double eps = 1e-10;
const double pi = acos(-1.0);
const int maxn = 1e6 + 10;

int n;

inline ll mul(ll a,ll b,ll mod){//O(1)取模快速乘,不会爆long long
	return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;
}

ll exgcd(ll a, ll b, ll& x, ll& y){
	if(!b){
		x = 1, y = 0;
		return a;
	}
	ll d = exgcd(b,a%b,y,x);
	y -= a/b*x;
	return d;
}
ll a[maxn],m[maxn];
ll solve(){
	ll a1,m1;
	m1=m[1],a1=a[1];
	for(int i = 2; i <= n; i++){
		ll a2,m2,k1,k2;
		m2=m[i],a2=a[i];
		ll d = exgcd(m1,m2,k1,k2);
		if((a2-a1)%d) return -1;
		else{
			k1 = mul(k1,(a2-a1)/d,m2/d);//这个地方必须要用取模快速乘
			a1 = a1+k1*m1;
			m1 = abs(m1/d*m2);
		}
	}
	return (a1%m1+m1)%m1;
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
		scanf("%lld%lld",&m[i],&a[i]);
	printf("%lld\n",solve());
	return 0;
}

z函数(扩展kmp)

O(n)

#include <iostream>
#include <vector>
#include <queue>
#include <set>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <numeric>
#include <stack>
#include <map>
#include <bitset>
using namespace std;
#define repeat(n) for(int ___ghuhd = (n); ___ghuhd; --___ghuhd)
#define loop(i, l, r) for(int i = (l); i <= (r); ++i)
#define rev_loop(i, r, l) for(int i = (r); i >= (l); --i)
template<typename T>
inline void MIN(T& a, T b) { a = min(a, b); }
template<typename T>
inline void MAX(T& a, T b) { a = max(a, b); }
int read(){
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) c=='-'?f=-1:1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
#define READ(...) int __VA_ARGS__; INIT_BY_READ(__VA_ARGS__)
void INIT_BY_READ(int& a) { a = read(); }
template<typename... Args>
void INIT_BY_READ(int& a, Args&... args) {
    a = read(); INIT_BY_READ(args...);
}
using LL = long long;
using PA = pair<int, int>;
#define int long long
const int maxn=2e7+100;
int n,m;
char s[maxn],t[maxn];
int z[maxn];
int p[maxn];
signed main(){
	cin>>(s)>>(t);
	n=strlen(s);
	m=strlen(t);
	z[0]=m;
	for(int i=1,l=0,r=0;i<m;i++){
		if(i<=r&&z[i-l]<r-i+1){
			z[i]=z[i-l];
		}
		else{
			z[i]=max(0ll,r-i+1);
			while(i+z[i]<m&&t[z[i]]==t[z[i]+i])z[i]++;
		}
		if(i+z[i]-1>r)l=i,r=i+z[i]-1;
	}
	while(s[p[0]]==t[p[0]])p[0]++;
	for(int i=1,l=0,r=0;i<n;i++){
		if(i<=r&&z[i-l]<r-i+1)
			p[i]=z[i-l];
		else{
			p[i]=max(0ll,r-i+1);
			while(i+p[i]<n&&p[i]<m&&t[p[i]]==s[p[i]+i])p[i]++;
		}
		if(i+p[i]-1>r)l=i,r=i+p[i]-1;
	}
	int ans=0;
	for(int i=0;i<m;i++)
		ans^=(z[i]+1)*(i+1);
	cout<<ans<<endl;
	ans=0;
	for(int i=0;i<n;i++)
		ans^=(p[i]+1)*(i+1);
	cout<<ans<<endl;
	return 0;
}
posted @ 2022-11-18 09:29  xyc1719  阅读(46)  评论(0)    收藏  举报