大板子 —— 1

博客介绍了STL与多种黑科技函数,包括输入输出挂、高精度除法、位运算函数、整行读入字符串方法、随机数生成、字符串与整数转换、全排列函数、BM算法、快速乘、环检测算法等,还提及了unordered_map性能优化与重载、C++ bitset用法、String函数及快速阶乘算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

——— STL与黑科技函数 ———

头文件

#include <iostream> <fstream> <sstream> <cstdlib> <cstdio> <cmath> <string>
#include <cstring> <algorithm> <queue> <stack> <vector> <set> <map> <list>
#include <iomanip> <cctype> <cassert> <bitset> <ctime> <climits> <complex>
//#include <chrono> <random> <unordered_set> <unordered_map>

#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimization("unroll-loops")

—— 输入输出挂 ——

简易版

const int MAXSIZE=100000;
char buf[MAXSIZE],*p1=buf,*p2=buf;
char obuf[1<<23],*O=obuf;
#define nc p1==p2&&(p2=(p1=buf)+fread\
(buf,1,MAXSIZE,stdin),p1==p2)?EOF:*p1++
inline void read(int &x) {
	bool f=0; char ch=nc; x=0;
	while(!isdigit(ch)) {if(ch=='-')f=1;ch=nc;}
	while(isdigit(ch)) x=x*10+ch-'0',ch=nc;
	if(f) x=-x;
}
inline void print(int x) {
	if(x<0) *O++='-', x=-x;
    if(x>9) print(x/10);
    *O++=x%10+'0';
}
fwrite(obuf,O-obuf,1,stdout);	// 末尾 

爆炸版

namespace IO{
#define BUF_SIZE 100000
#define OUT_SIZE 100000
#define ll long long
    bool IOerror=0;
    inline char nc(){
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if (p1==pend){
            p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if (pend==p1){IOerror=1;return -1;}
        }
        return *p1++;
    }
    inline bool blank(char ch){return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';}
    inline void read(int &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        if (ch=='-')sign=1,ch=nc();
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
        if (sign)x=-x;
    }
    inline void read(ll &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        if (ch=='-')sign=1,ch=nc();
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
        if (sign)x=-x;
    }
    inline void read(double &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        if (ch=='-')sign=1,ch=nc();
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
        if (ch=='.'){
            double tmp=1; ch=nc();
            for (;ch>='0'&&ch<='9';ch=nc())tmp/=10.0,x+=tmp*(ch-'0');
        }
        if (sign)x=-x;
    }
    inline void read(char *s){
        char ch=nc();
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        for (;!blank(ch)&&!IOerror;ch=nc())*s++=ch;
        *s=0;
    }
    inline void read(char &c){
        for (c=nc();blank(c);c=nc());
        if (IOerror){c=-1;return;}
    }
    //fwrite->write
    struct Ostream_fwrite{
        char *buf,*p1,*pend;
        Ostream_fwrite(){buf=new char[BUF_SIZE];p1=buf;pend=buf+BUF_SIZE;}
        void out(char ch){
            if (p1==pend){
                fwrite(buf,1,BUF_SIZE,stdout);p1=buf;
            }
            *p1++=ch;
        }
        void print(int x){
            static char s[15],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1);
        }
        void println(int x){
            static char s[15],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1); out('\n');
        }
        void print(ll x){
            static char s[25],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1);
        }
        void println(ll x){
            static char s[25],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1); out('\n');
        }
        void print(double x,int y){
            static ll mul[]={1,10,100,1000,10000,100000,1000000,10000000,100000000,
                             1000000000,10000000000LL,100000000000LL,1000000000000LL,
			10000000000000LL,100000000000000LL,1000000000000000LL,
			10000000000000000LL,100000000000000000LL};
            if (x<-1e-12)out('-'),x=-x;x*=mul[y];
            ll x1=(ll)floor(x); if (x-floor(x)>=0.5)++x1;
            ll x2=x1/mul[y],x3=x1-x2*mul[y]; print(x2);
            if (y>0){out('.'); for (size_t i=1;i<y&&x3*mul[i]<mul[y];out('0'),++i); print(x3);}
        }
        void println(double x,int y){print(x,y);out('\n');}
        void print(char *s){while (*s)out(*s++);}
        void println(char *s){while (*s)out(*s++);out('\n');}
        void flush(){if (p1!=buf){fwrite(buf,1,p1-buf,stdout);p1=buf;}}
        ~Ostream_fwrite(){flush();}
    }Ostream;
    inline void print(int x){Ostream.print(x);}
    inline void println(int x){Ostream.println(x);}
    inline void print(char x){Ostream.out(x);}
    inline void println(char x){Ostream.out(x);Ostream.out('\n');}
    inline void print(ll x){Ostream.print(x);}
    inline void println(ll x){Ostream.println(x);}
    inline void print(double x,int y){Ostream.print(x,y);}
    inline void println(double x,int y){Ostream.println(x,y);}
    inline void print(char *s){Ostream.print(s);}
    inline void println(char *s){Ostream.println(s);}
    inline void println(){Ostream.out('\n');}
    inline void flush(){Ostream.flush();}
#undef ll
#undef OUT_SIZE
#undef BUF_SIZE
};
using namespace IO;

—— 高精度除法 ——

注意结果保存在 aaa 数组里,用 a[0]a[0]a[0] 进行加减,局限于先加减后除

void div(int x){
    ll tmp = 0;
    for(int i=0; i<=p; i++){
        tmp = tmp * 1000000000 + a[i];
		a[i] = tmp / x; tmp %= x;
    }
}
	printf("%d.", a[0]);
    for(int i=1; i<=p; i++) printf("%09d", a[i]);

—— STL —— 位运算函数之 __builtin_ ——

1. __builtin_popcount(unsigned int n)

判断 nnn 的二进制中有多少个 111

int n = 15;	//二进制为1111
cout<< __builtin_popcount(n) <<endl;	//输出4

2. __builtin_ffs(unsigned int n)

判断 nnn 的二进制末尾最后一个 111 的位置

int n = 1;	//1
int m = 8;	//1000
cout<< __builtin_ffs(n) <<endl;	//输出1
cout<< __builtin_ffs(m) <<endl;	//输出4

3. __builtin_ctz(unsigned int n)

判断 nnn 的二进制末尾后面 000 的个数

int n = 1;	//1
int m = 8;	//1000
cout<< __builtin_ctzll(n) <<endl;	//输出0
cout<< __builtin_ctz(m) <<endl;	//输出3

4. __builtin_clz(unsigned int n)

返回前导的 000 的个数

PS:那么用此方法可以计算出第一个1的位置

int n = 1;	//1
int m = 8;	//1000
cout<< 32 - __builtin_clz(n) <<endl;	//输出1
cout<< 64 - __builtin_clzll(m) <<endl;	//输出4

5. __builtin_parity(unsigned int n)

判断 nnn 的二进制中 111 的个数的奇偶性

int n = 15;	//二进制为1111
int m = 7;	//111
cout<< __builtin_parity(n) <<endl;	//偶数个,输出0
cout<< __builtin_parity(m) <<endl;	//奇数个,输出1

—— C/C++整行读入字符串 ——

方法一:scanf() 读入 char[]

    使用方法:

char str[1024];
scanf("%[^\n]", &str);
getchar();

    说明:在scanf函数中,可以使用%c来读取一个字符,使用%s读取一个字符串, 但是读取字符串时不忽略空格,读字符串时忽略开始的空格,并且读到空格为止,因此只能读取一个单词,而不是整行字符串。

    其实scanf函数也可完成这样的功能,而且还更强大。这里主要介绍一个参数,%[ ],这个参数的意义是读入一个字符集合。[ ]是个集合的标志,因此%[ ]特指读入此集合所限定的那些字符,比如%[A-Z]是输入大写字母,一旦遇到不在此集合的字符便停止。如果集合的第一个字符是"^",这说明读取不在"^"后面集合的字符,既遇到"^"后面集合的字符便停止。注意此时读入的字符串是可以含有空格的,而且会把开头的空格也读进来。

    注意:如果要循环的多次从屏幕上读取一行的话,就要在读取一行后,在用%c读取一个字符,将输入缓冲区中的换行符给读出来。否则的话,在下一次读取一行的时候,第一个就遇到'\n',匹配不成功就直接返回了。这里可以scanf()或者getchar()函数读取换行符。


方法二:getchar()读入char[]

    使用方法:

char str[1024];
int i = 0;
while((str[i]=getchar())!='\n') i++;
getchar();

    说明:这样一个一个读也可以,也会把开头的空格读进来。最后也需要考虑换行符,使用getchar()读出来。


方法三:gets()读入char[]

    使用方法:

char str[1024];
gets(str);

    说明:感觉这个就是多个getchar的集合函数,很好用。功能是从标准输入键盘上读入一个完整的行(从标准输入读,一直读到遇到换行符),把读到的内容存入括号中指定的字符数组里,并用空字符'\0'取代行尾的换行符'\n'读入时不需要考虑换行符。


方法四:getline()读入string或char[]

    使用方法:

string str;
getline(cin, str);	//读入string

char str2[1024];
cin.getline(str2, 1024);	//读入char数组

    说明:这是比较常用的方法,cin.getline第三个参数表示间隔符,默认为换行符'\n'读入不需要考虑最后的换行符。


方法五:get()读入char[]

    使用方法:

char str3[1024];
cin.get(str3,1024);//读入char数组

    说明:get函数读入时需要考虑最后的换行符,也就是说,如果用get读入多行数据,要把'\n'另外读出来,一般使用cin.get(str,1024).get();来读入多组数据。


—— mt19937 ——
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());

rnd()rnd()rnd() 即可,括号内部是随机种子,也可以换成下面这个:

mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());

random_shuffle()可以替换为:

shuffle(a+1, a+n+1, rnd);

—— atoi() 与 itoa()函数用法 ——

atoi() 函数的原型为: int atoi(const char *str );

函数功能:把字符串转换成整型数
参数str:要进行转换的字符串
返回值:每个函数返回 int 值,此值由将输入字符作为数字解析而生成。 如果该输入无法转换为该类型的值,则atoi的返回值为 0


itoa() 函数的原型为: char *itoa( int value, char *string,int radix);

value:要转换的数据
string:目标字符串的地址
radix:转换后的进制数,可以是10进制、16进制等,范围必须在 2~36

下面是一个十进制转八进制的方法:

int main(){
	int num = 10;
	char str[100];
	itoa(num, str, 8);      //将整数10转换为八进制保存在str字符数组中
	printf("%s\n", str);
	system("pause");
	return 0;
}

下面是一个十进制转二进制的方法:

int main(){
	int num = 15;
	char str[100];
	int n = atoi(itoa(num, str, 2));   //先转换为二进制的字符串,再换为整数
	printf("%d\n",n);
	system("pause");
}

—— next_permutation() 全排列函数 ——

上一个排列即为prev_permutation(int *begin, int *end)

int main() {
	char a[3]= {'a','b','c'}; 
	for(int i=1; i<=6; i++) { 
		for(int j=0; j<3; j++) printf("%c ", a[j]);
		printf("\n");
		next_permutation(a, a+3);
	}
}

—— BM算法 ——

模为质数

#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod = 1e9 + 7;	// 注意替换 
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0);\
 for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}

namespace linear_seq{
    const int N=10010;
    ll res[N],base[N],_c[N],_md[N];
    vector<ll> Md;
    void mul(ll *a,ll *b,int k) {
        rep(i,0,k+k) _c[i]=0;
        rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
        for (int i=k+k-1;i>=k;i--) if (_c[i])
            rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
        rep(i,0,k) a[i]=_c[i];
    }
    int solve(ll n,VI a,VI b) {// a 系数 b 初值 b[n+1]=a[0]*b[n]+...
        ll ans=0,pnt=0;
        int k=SZ(a);
        assert(SZ(a)==SZ(b));
        rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
        Md.clear();
        rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
        rep(i,0,k) res[i]=base[i]=0;
        res[0]=1;
        while ((1ll<<pnt)<=n) pnt++;
        for (int p=pnt;p>=0;p--) {
            mul(res,res,k);
            if ((n>>p)&1) {
                for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
                rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
            }
        }
        rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
        if (ans<0) ans+=mod;
        return ans;
    }
    VI BM(VI s) {
        VI C(1,1),B(1,1);
        int L=0,m=1,b=1;
        rep(n,0,SZ(s)) {
            ll d=0;
            rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
            if (d==0) ++m;
            else if (2*L<=n) {
                VI T=C;
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                L=n+1-L; B=T; b=d; m=1;
            } else {
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                ++m;
            }
        }
        return C;
    }
    int gao(VI a,ll n){
        VI c=BM(a);
        c.erase(c.begin());
        rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
        return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
    }
};

int main() {
	ll n; vector <int> v;
    v.push_back(0); v.push_back(0);
    v.push_back(3); v.push_back(30);
    v.push_back(195); v.push_back(1050);
    v.push_back(5103);
    while(~scanf("%lld", &n)) {
        printf("%lld\n",linear_seq::gao(v,n-1)%mod);
    }
}

—— 快速乘 + Montgomery modular multiplication ——

O(logn):O(logn):Ologn:

ll mult(ll a, ll b, ll p) {
    a %= p; b %= p;
    ll ret = 0;
    while(b) {
        if(b & 1) ret = (ret + a) % p;
        a = (a + a) % p;
        b >>= 1;
    }
    return ret;
}

O(1):O(1):O1:

ll qmul(ll a, ll b) {
    return (a*b - (ll)((long double)a/mod*b)*mod+mod)%mod;
}

Montgomery modular multiplication:

const int mod = 1e9 + 7;
typedef unsigned long long ull;
typedef __uint128_t L;
struct FastMod {
	ull b, m;
	FastMod(ull b) : b(b), m(ull((L(1) << 64) / b)) {}
	inline ull reduce(ull a) { 
		return a - (ull)((L(m) * a) >> 64) * b;
	}
} F(mod);

—— Floyd’s Tortoise and Hare & 环检测算法 ——

简介

检测一个链表是否有环(循环节),如果有,确定环的起点以及环的长度


复杂度

时间复杂度:O(n+m)O(n+m)O(n+m)
空间复杂度:O(1)O(1)O(1)
nnn 为起点 SSS 到环入口 PPP 的距离,mmm 为环的长度


结论

  ①①:环检测
  两个指针 ttthhhttt 移动速度为 111hhh 移动速度为 222,判断是否相遇

  ②②:找出环的入口节点
  两个指针相遇后,将 ttt 重新放到链表头,然后两个指针速度都为 111,再次相遇后,所在位置即为入口节点

  ③③:计算环长度
  两个指针都放在入口节点后,ttt 移动速度为 111hhh 移动速度为 000,再次相遇,即可计算出环长度


在这里插入图片描述

计算入口节点及环长度:

const ll mod = (1ll << 40);
inline ll nxt(ll x){
	return (x + (x >> 20) + 12345ll) % mod;
}

signed main() {
	ll s0 = 0x600DCAFE, cnt = 0, num = 0;
	ll t = s0, h = s0;
	while(true){
		t = nxt(t), h = nxt(nxt(h));
		++cnt;
		if(!(t & 1)) ++num;
		if(t == h) break;
	}
	
	t = s0, cnt = num = 0;
	while(true){
		t = nxt(t), h = nxt(h);
		++cnt;
		if(!(t & 1)) ++num;
		if(t == h) break;
	}
	ll st_cyc = cnt, st_num = num;
	
	cnt = num = 0;
	while(true){
		t = nxt(t);
//		h = nxt(h);
		++cnt;
		if(!(t & 1)) ++num;
		if(t == h) break;
	}
	ll len_cyc = cnt, ans_cyc = num;
	
	deb(st_cyc); deb(st_num); 
	puts("-----------");
	deb(len_cyc); deb(ans_cyc);
}

—— unordered_map 性能优化 与 重载 ——

insert / clear 性能优化

当插入元素过多时,发生了哈希碰撞,碰撞开链到一定的阈值,触发了增加 bucketbucketbucket,进而触发了 rehashrehashrehash

因此,reservereservereserve 可以用来预留元素个数,rehashrehashrehash 根据提供的元素个数预留足够的bucket数目

unordered_map <int, int> mp;
mp.reserve(2e7);
mp.rehash(2e7); 

同时,unordered_mapunordered\_mapunordered_map 每清空一次会遍历删除所有内容,可以替换为 swapswapswap

unordered_map <int, int> mp1;
mp.swap(mp1);

时间复杂度即可降为 O(1)O(1)O(1)


重载

unordered_mapunordered\_mapunordered_map 是哈希表的机制,也就是每个 keykeykey 会生成一个哈希码,根据哈希码来判断元素是否相同,故必须提供产生哈希码的函数,但是哈希码相同并不一定代表两个元素相同,所以还要实现 ====== 操作符。

以下用三种方式重写哈希:
①:手动重写
②:调用 boostboostboost 库函数(要手动下载 boostboostboost 库)
③:简单哈希

template <typename T>
inline void hash_combine(std::size_t &seed, const T &val) {
    seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
// auxiliary generic functions to create a hash value using a seed
template <typename T> inline void hash_val(std::size_t &seed, const T &val) {
    hash_combine(seed, val);
}
template <typename T, typename... Types>
inline void hash_val(std::size_t &seed, const T &val, const Types &... args) {
    hash_combine(seed, val);
    hash_val(seed, args...);
}

template <typename... Types>
inline std::size_t hash_val(const Types &... args) {
    std::size_t seed = 0;
    hash_val(seed, args...);
    return seed;
}

struct node {
	string str;
	int num;
	node(string _str, int _num):str(_str), num(_num) {}
	bool operator == (const node &A) const {
		return (str==A.str && num==A.num);
	}
};
struct node_hash {
	size_t operator() (const node &A) const {
		return hash_val(A.str, A.num);	// 重写 
//		return hash<string>()(A.str) ^ hash<int>()(A.num);	// 简单 
	}
};
unordered_map <node, int, node_hash> mp;


signed main() {
	mp[node("wpf", 666)] = 11;
	mp[node("zzz", 123)] = 22;
	printf("%d\n", (*mp.begin()).first.num);
	printf("%d\n", (*mp.begin()).second);

	deb(mp[node("zzz", 123)]);
}

—— C++ bitset 用法 ——

常用函数

	bit.size()       返回大小(位数)
	bit.count()     返回1的个数
	bit.any()       返回是否有1
	bit.none()      返回是否没有1
	bit.set()       全都变成1
	bit.set(p)      将第p + 1位变成1(bitset是从第0位开始的!) 
	bit.set(p, x)   将第p + 1位变成x
	bit.reset()     全都变成0
	bit.reset(p)    将第p + 1位变成0
	bit.flip()      全都取反
	bit.flip(p)     将第p + 1位取反
	bit.to_ulong()  返回它转换为unsigned long的结果,如果超出范围则报错
	bit.to_ullong() 返回它转换为unsigned long long的结果,如果超出范围则报错
	bit.to_string() 返回它转换为string的结果

构造函数

	bitset<4> bitset1;  //无参构造,长度为4,默认每一位为0

    bitset<8> bitset2(12);  //长度为8,二进制保存,前面用0补充

    string s = "100101";
    bitset<10> bitset3(s);  //长度为10,前面用0补充
    
    char s2[] = "10101";
    bitset<13> bitset4(s2);  //长度为13,前面用0补充

注意:

用字符串构造时,字符串只能包含 ‘0’ 或 ‘1’ ,否则会抛出异常。

构造时,需在<>中表明bitset 的大小(即size)。

在进行有参构造时,若参数的二进制表示比bitset的size小,则在前面用0补充(如上面的栗子);若比bitsize大,参数为整数时取后面部分,参数为字符串时取前面部分(如下面栗子):

	bitset<2> bitset1(12);  

    string s = "100101";  
    bitset<4> bitset2(s);  //s的size=6,而bitset的size=4,只取前面部分,即1001

    char s2[] = "11101";
    bitset<4> bitset3(s2);  //与bitset2同理,只取前面部分,即1110

    cout << bitset1 << endl;  //00
    cout << bitset2 << endl;  //1001
    cout << bitset3 << endl;  //1110

可用函数

	bitset<8> foo ("10011011");

    cout << foo.count() << endl;  //5  (count函数用来求bitset中1的位数,foo中共有5个1
    cout << foo.size() << endl;   //8  (size函数用来求bitset的大小,一共有8位

    cout << foo.test(0) << endl;  //true  (test函数用来查下标处的元素是0还是1
    cout << foo.test(2) << endl;  //false  (同理,foo[2]为0,返回false

    cout << foo.any() << endl;  //true  (any函数检查bitset中是否有1
    cout << foo.none() << endl;  //false  (none函数检查bitset中是否没有1
    cout << foo.all() << endl;  //false  (all函数检查bitset中是全部为1

补充说明一下:test函数会对下标越界作出检查,而通过 [ ] 访问元素却不会经过下标检查,所以,在两种方式通用的情况下,选择test函数更安全一些

另外,含有一些函数:

	bitset<8> foo ("10011011");

    cout << foo.flip(2) << endl;  //10011111  (用于将参数位取反
    cout << foo.flip() << endl;   //01100000  (将bitset每一位全部取反

    cout << foo.set() << endl;    //11111111  (将bitset的每一位全部置为1
    cout << foo.set(3,0) << endl;  //11110111  (将第一参数位的元素置为第二参数的值
    cout << foo.set(3) << endl;    //11111111  (将参数下标处置为1

    cout << foo.reset(4) << endl;  //11101111  (将参数下标处置为0
    cout << foo.reset() << endl;   //00000000  (将bitset的每一位全部置为0

同样,它们也都会检查下标是否越界,如果越界就会抛出异常


—— String 函数 ——

1. substr()

对字符串进行截取

	string s = "0123456789";

	string sub1 = s.substr(5);		// 从下标为5开始一直到结尾:sub1 = "56789"
	string sub2 = s.substr(5, 3);	// 从下标为5开始截取长度为3位:sub2 = "567"

2. c_str()

返回一个指向正规 CCC 字符串的指针, 内容与本 stringstringstring 串相同
注意这是一个临时指针

	char str[20];
	string s = "1234";
	strcpy(str, s.c_str());		// 将 s拷贝到 str 
	
	printf("%s", s.c_str());	// 可以直接输出 

3. replace()

用法一:
strstrstr 替换指定字符串从起始位置 pospospos 开始,长度为 lenlenlen 的字符
string& replace (size_t pos, size_t len, const string& str);

	string s = "this@ is@ a test string!";
	s = s.replace(s.find("@"), 1, ""); 	// 找到第一个@位置,替换长度为1的串为空 
	 
    cout << s << endl; 		// "this is@ a test string!"

用法二:
strstrstr 替换 迭代器起始位置 到 结束位置 的字符
string& replace (const_iterator i1, const_iterator i2, const string& str);

	string s = "this@ is@ a test string!";
	s = s.replace(s.begin(), s.begin()+6, ""); 	// 用str替换从begin位置开始的6个字符  
	 
    cout << s << endl; 		// "is@ a test string!"

用法三:
substrsubstrsubstr 的指定子串(给定起始位置和长度)替换从指定位置上的字符串
string& replace (size_t pos, size_t len, const string& str, size_t subpos, size_t sublen);

    string s = "this@ is@ a test string!";  
    string substr = "12345";  
    // 用substr的指定子串(从1位置数共3个字符)替换从0到5位置上的 s
    s = s.replace(0, 5, substr, substr.find("1"), 3); 
    
	cout << s << endl;		// "123 is@ a test sting!"

4. replace_all()

replace_all() : 进行字符串替换,并循环换到没有为止
replace_all_distinct : 进行字符串替换,只换一次

string& replace_all(string& str, const string& old_value, const string& new_value) {
	while(true) {
		string::size_type pos(0);
		if( (pos=str.find(old_value))!=string::npos )
			str.replace(pos, old_value.length(), new_value);
		else break;
	}
	return str;
}
string& replace_all_distinct(string& str, const string& old_value, string& new_value) {
	for(string::size_type pos(0); pos!=string::npos; pos+=new_value.length()) {
		if( (pos=str.find(old_value, pos))!=string::npos )
			str.replace(pos, old_value.length(), new_value);
		else break;
	}
	return str;
}
	string str1 = "12212", str2 = "12212";
	
	cout << replace_all(str1, "12", "21") << endl;			// "22211"
    cout << replace_all_distinct(str2, "12", "21") << endl;	// "21221"

—— 快速阶乘算法 ——

求: n! mod p\large n! \text{ mod } pn! mod p
时间复杂度:Θ(nlog⁡n)\Theta(\sqrt n \log n)Θ(nlogn)

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;

struct Fast_fac {
	static const int N=(1<<17)+5;int P;
	inline int add(R int x,R int y){return 0ll+x+y>=P?0ll+x+y-P:x+y;}
	inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
	inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
	int ksm(R int x,R int y){
		R int res=1;
		for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
		return res;
	}
	const double Pi=acos(-1.0);
	struct cp{
		double x,y;
		inline cp(){}
		inline cp(R double xx,R double yy):x(xx),y(yy){}
		inline cp operator +(const cp &b)const{return cp(x+b.x,y+b.y);}
		inline cp operator -(const cp &b)const{return cp(x-b.x,y-b.y);}
		inline cp operator *(const cp &b)const{return cp(x*b.x-y*b.y,x*b.y+y*b.x);}
		inline cp operator *(const double &b)const{return cp(x*b,y*b);}
		inline cp operator ~()const{return cp(x,-y);}
	}w[2][N];
	int r[21][N],ifac[N],lg[N],inv[N];double iv[21];
	void Pre(){
		iv[0]=1;
		fp(d,1,17){
			fp(i,0,(1<<d)-1)r[d][i]=(r[d][i>>1]>>1)|((i&1)<<(d-1));
			lg[1<<d]=d,iv[d]=iv[d-1]*0.5;
		}
		inv[0]=inv[1]=ifac[0]=ifac[1]=1;
		fp(i,2,131072)inv[i]=mul(P-P/i,inv[P%i]),ifac[i]=mul(ifac[i-1],inv[i]);
		for(R int i=1,d=0;i<131072;i<<=1,++d)fp(k,0,i-1)
			w[1][i+k]=cp(cos(Pi*k*iv[d]),sin(Pi*k*iv[d])),
			w[0][i+k]=cp(cos(Pi*k*iv[d]),-sin(Pi*k*iv[d]));
	}
	int lim,d;
	void FFT(cp *A,int ty){
		fp(i,0,lim-1)if(i<r[d][i])swap(A[i],A[r[d][i]]);
		cp t;
		for(R int mid=1;mid<lim;mid<<=1)
			for(R int j=0;j<lim;j+=(mid<<1))
				fp(k,0,mid-1)
					A[j+k+mid]=A[j+k]-(t=w[ty][mid+k]*A[j+k+mid]),
					A[j+k]=A[j+k]+t;
		if(!ty)fp(i,0,lim-1)A[i]=A[i]*iv[d];
	}
	void MTT(int *a,int *b,int len,int *c){
	    static cp f[N],g[N],p[N],q[N];
	    lim=len,d=lg[lim];
	    fp(i,0,len-1)f[i]=cp(a[i]>>16,a[i]&65535),g[i]=cp(b[i]>>16,b[i]&65535);
	    fp(i,len,lim-1)f[i]=g[i]=cp(0,0);
	    FFT(f,1),FFT(g,1);
	    fp(i,0,lim-1){
	        cp t,f0,f1,g0,g1;
	        t=~f[i?lim-i:0],f0=(f[i]-t)*cp(0,-0.5),f1=(f[i]+t)*0.5;
	        t=~g[i?lim-i:0],g0=(g[i]-t)*cp(0,-0.5),g1=(g[i]+t)*0.5;
	        p[i]=f1*g1,q[i]=f1*g0+f0*g1+f0*g0*cp(0,1);
	    }
	    FFT(p,0),FFT(q,0);
	    fp(i,0,lim-1)c[i]=((((ll)(p[i].x+0.5)%P<<16)%P<<16)+\
	    		((ll)(q[i].x+0.5)<<16)+((ll)(q[i].y+0.5)))%P;
	}
	void calc(int *a,int *b,int n,int k){
		static int f[N],g[N],h[N],sum[N],isum[N];
		int len=1;while(len<=n+n)len<<=1;
		fp(i,0,n)f[i]=mul(a[i],mul(ifac[i],ifac[n-i]));
		for(R int i=n-1;i>=0;i-=2)f[i]=P-f[i];
		int t=dec(k,n);
		fp(i,0,n+n)g[i]=add(i,t);
		sum[0]=g[0];fp(i,1,n+n)sum[i]=mul(sum[i-1],g[i]);
		isum[n+n]=ksm(sum[n+n],P-2);
		fd(i,n+n,1)isum[i-1]=mul(isum[i],g[i]);
		fp(i,1,n+n)g[i]=mul(isum[i],sum[i-1]);g[0]=isum[0];
		fp(i,n+1,len-1)f[i]=0;fp(i,n+n+1,len-1)g[i]=0;
		
		MTT(f,g,len,h);
		int res=1,p1=k-n,p2=k;
		fp(i,p1,p2)res=1ll*res*i%P;
		res=dec(res,0);
		
		fp(i,0,n)g[i]=(0ll+P+p1+i)%P;
		sum[0]=g[0];fp(i,1,n)sum[i]=mul(sum[i-1],g[i]);
		isum[n]=ksm(sum[n],P-2);
		fd(i,n,1)isum[i-1]=mul(isum[i],g[i]);
		fp(i,1,n)g[i]=mul(isum[i],sum[i-1]);g[0]=isum[0];
		
		for(R int i=0;i<=n;p2=add(p2,1),++i)
			b[i]=mul(h[i+n],res),res=mul(res,mul(g[i],p2+1));
	}
	int solve(int bl){
		static int a[N],b[N],c[N];
		int s=0;for(int p=bl;p;p>>=1)++s;a[0]=1,--s;
		int qwq=ksm(bl,P-2);
		for(int p=0;s>=0;--s){
			if(p){
				calc(a,b,p,p+1);
				fp(i,0,p)a[p+i+1]=b[i];a[p<<1|1]=0;
				calc(a,b,p<<1,mul(p,qwq));
				p<<=1;fp(i,0,p)a[i]=mul(a[i],b[i]);
			}
			if(bl>>s&1){
				fp(i,0,p)a[i]=mul(a[i],(1ll*bl*i+p+1)%P);
				p|=1,a[p]=1;
				fp(i,1,p)a[p]=mul(a[p],(1ll*bl*p+i)%P);
			}
		}
		int res=1;
		fp(i,0,bl-1)res=mul(res,a[i]);
		return res;
	}
	int GetFac(int n){
		int s=sqrt(n),res=solve(s);
		fp(i,s*s+1,n)res=mul(res,i);
		return res;
	}
	int Fac(int n){
		if(n>P-1-n){
			int res=ksm(GetFac(P-1-n),P-2);
			return (P-1-n)&1?res:P-res;
		}
		return GetFac(n);
	}
} sol;

int main(){
	int n, P, T;
	scanf("%d", &T);
	while(T--) {
		scanf("%d%d", &n, &P);
		sol.P = P, sol.Pre();
		printf("%d\n", sol.Fac(n));
	} 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值