Team_GJX模板整理
哈希
康拓展开
int cantor(int key,int fac[],int n){
int result,temp[n];
for(int i=n-1;i>=0;i--){
temp[i]=key%10;
key=key/10;
}
result=0;
for(int i=0;i<n-1;i++){
int tot=0;
for(int j=i+1;j<n;j++){
if(temp[j]<temp[i]) tot++;
}
result+=tot*fac[n-1-i];
}
return result;
}
BKDR Hash
unsigned int BKDRHash(char *str){
unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
unsigned int hash = 0;
while (*str){
hash = hash * seed + (*str++);
}
return (hash & 0x7FFFFFFF);
}
AP hash
unsigned int APHash(char *str){
unsigned int hash = 0;
int i;
for (i=0; *str; i++){
if ((i & 1) == 0){
hash ^= ((hash << 7) ^ (*str++) ^ (hash >> 3));
}else{
hash ^= (~((hash << 11) ^ (*str++) ^ (hash >> 5)));
}
}
return (hash & 0x7FFFFFFF);
}
字符串hash
$HASH[i]=S[1]xi+S[2]x+...+S[i]$.
对于$[l,r]$区间的hash值为$HASH(l,r)=HASH[r]-HASH[l-1]x^{r-l+1}$.
typedef unsigned long long ull;
const int N = 100000 + 5;
const ull base = 163;
char s[N];
ull hash[N];
void init(){//处理 hash 值
p[0] = 1;
hash[0] = 0;
int n = strlen(s + 1);
for(int i = 1; i <=100000; i ++)p[i] =p[i-1] * base;
for(int i = 1; i <= n; i ++)hash[i] = hash[i - 1] * base + (s[i] - 'a');
}
ull get(int l, int r, ull g[]){//取出 g 里 l - r 里面的字符串的 hash 值
return g[r] - g[l - 1] * p[r - l + 1];
}
数论
快速乘
ll Mul(ll a,ll b,ll base){
ll ans = 0;
a %= base;b %= base;
ll now = b;
while (a != 0){
ans += now * (a % 2);
ans %= base;
now *= 2;
now %= base;
a = a / 2;
}
return ans;
}
求原根
vector<ll> c;
inline bool pan_g(ll g, ll p) {
for (int i = 0; i < c.size(); ++i)
if (qm(g, c[i], p) == 1)
return 0;
return 1;
}
inline ll findg(ll p) {
c.clear();
ll tmp = p - 1;
ll k = 2;
while (k * k <= tmp) {
if (tmp % k == 0) {
c.push_back(k);
while (tmp % k == 0)
tmp /= k;
}
++k;
}
if (tmp != 1)
c.push_back(tmp);
for (int i = 0; i < c.size(); ++i)
c[i] = (p-1) / c[i];
ll g = 1;
while (true) {
if (pan_g(g, p)) {
return g;
}
++g;
}
return 0;
}
欧拉函数
int getphi(int p){
int phi = p, m = p, k = 2;
while (k * k <=m) {
if (m % k == 0) {
phi /= k;
phi *= (k-1);
while (m % k == 0)
m /= k;
}
++k;
}
if (m != 1) {
phi /= m;
phi *= (m-1);
}
return phi;
}
扩展欧几里得
int exgcd(int a,int b,int&x,int&y){
if(!b)return x=1,y=0,a;
int d=exgcd(b,a%b,x,y),t=x;
return x=y,y=t-a/b*y,d;
}
线性筛
p 为是否为素数 prime 为第几个素数 phi 为欧拉函数 mu 为莫比乌斯函数 divnum 为因子 数 nxt 为最大的质因子的编号 e 为最大的质因子的幂次 d 为 i 除去最小质因子的数 F 为 i 的因数平方和
memset(p, 0, sizeof(p));
phi[1] = 1;
mu[1] = 1;
div_num[1] = 1;
F[1] = 1;
top = 0;
for (i = 2; i <= N; ++i) {
if (!p[i]) {
prime[++top] = i;
mu[i] = -1;
phi[i] = i-1;
e[i] = 1;
div_num[i] = 2;
d[i] = 1;
F[i] = ((ll)i * i + 1) % mod;
nxt[i] = top;//i 最大因子的下标
}
for (j = 1; j <= top; ++j) {
if (i * prime[j] > N)
break;
p[i * prime[j]] = 1;
nxt[i * prime[j]] = j;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
div_num[i*prime[j]]=div_num[i]/(e[i]+1)*(e[i]+2);
e[i*prime[j]]=e[i]+1;
d[i * prime[j]] = d[i];
F[i * prime[j]] = (F[i] * ((ll)prime[j] * prime[j] % mod) + F[d[i]]) % mod;
break;
}
phi[i * prime[j]] = phi[i] * phi[prime[j]];
mu[i * prime[j]] = -mu[i];
div_num[i*prime[j]]=div_num[i]*div_num[prime[j]];
e[i*prime[j]] = 1;
d[i * prime[j]] = i;
F[i * prime[j]] = F[i] * (((ll)prime[j] * prime[j] + 1) % mod) % mod;
}
}
中国剩余定理
$n$个同余方程,第$i$个为$x\equiv a[i] \ (\mod m[i])$,且$a[i]$两两互质,那么可以通过中国剩余定理合并。
#include <cstdio>
typedef long long ll;
const int maxn = 5;
ll a[maxn],m[maxn];
ll exgcd(ll a,ll b,ll&x,ll&y){
if(!b)return x=1,y=0,a;
ll d=exgcd(b,a%b,x,y),t=x;
return x=y,y=t-a/b*y,d;
}
ll CRT(ll *a,ll *m,ll n) {
ll ans = 0;
ll M = 21252;
for (int i=1;i<=n;i++) {
ll Mi=M/m[i];
ll t,y;
exgcd(Mi,m[i],t,y);
t=(t%m[i]+m[i])%m[i];//the minimal positive t
ans=(ans+a[i]*Mi*t)%M;
}
return ans;
}
int main(){
m[1]=23;m[2]=28;m[3]=33;
int cas=1,d;
while(~scanf("%lld%lld%lld%d",a+1,a+2,a+3,&d)){
if(a[1]==-1&&a[2]==-1&&a[3]==-1&&d==-1) break;
ll res=CRT(a,m,3ll);
res=(res-d)%21252;
if(res<=0)
res+=21252;
printf("Case %d: the next triple peak occurs in %d days.\n",cas++,res);
}
return 0;
}
扩展中国剩余定理
$x\equiv c1(\mod m1)$
$x\equiv c2(\mod m2)$
可以合并,条件为$(m1,m2)|(c2-c1)$.
$m=\frac{m1*m2}{(m1,m2)}$.
$c=inv(\frac{m1}{(m1,m2)},\frac{m2}{(m1,m2)})\frac{c2-c1}{(m1,m2)}\mod \frac{m2}{(m1,m2)}m1+c1$,
合并成$x\equiv c(\mod m)$.
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll&x,ll&y){
if(!b)return x=1,y=0,a;
ll d=exgcd(b,a%b,x,y),t=x;
return x=y,y=t-a/b*y,d;
}
int n;
ll ans, a1, a2, a3, r1, r2, r3, x, y, tmp;
int main() {
while (~scanf("%d", &n)) {
scanf("%lld%lld", &a1, &r1);
ans = (r1 % a1 + a1) % a1;
while (--n) {
scanf("%lld%lld", &a2, &r2);
if (ans == -1) continue;
tmp = exgcd(a1, a2, x, y);
if ((r1 - r2) % tmp != 0) {
ans = -1;
continue;
}
x = x * (r2 - r1) / tmp;
x = (x % a2 + a2) % a2;
r1 = r1 + a1 * x;
a1 = a1 * a2 / tmp;
ans = (r1 % a1 + a1) % a1;
}
printf("%lld\n", ans);
}
return 0;
}
BSGS
求$a^x\equiv b(\mod p)$的$x$解。
$m=\left \lceil \sqrt{p} \right \rceil$.
令$x=im-j$,化为$(am)i\equiv ba^j(\mod p)$.
$i=0,1,...,m$,求出$ba^i$,并记录对应$i$最大值。
$i=1,2,...,m$,寻找对应$(am)i\equiv before$,break输出答案。
#include <cstdio>
#include <cmath>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
ll a,b,p;//a^x=b(mod p)
bool flag;
int len;
struct node{
ll t,i;
}_hash[1<<16];
inline ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
inline ll power(ll a,ll n,ll p){
ll ret=1;ll now=a;
while(n!=0){
if(n&1)
ret=ret*now%p;
now=now*now%p;
n>>=1;
}
return ret;
}
bool comp(node A,node B){
if(A.t==B.t)
return A.i<B.i;
return A.t<B.t;
}
int Find(ll ret,int len){
int l=1,r=len;
int k=-1;
while(l<=r){
int mid=(l+r)>>1;
if(_hash[mid].t<ret)
l=mid+1;
else if(_hash[mid].t>ret)
r=mid-1;
else if(_hash[mid].t==ret)
l=mid+1,k=_hash[mid].i;
}
return k;
}
inline void BSGS(ll a,ll b,ll p){
if(/*gcd(a,p)!=1*/a % p == 0){//互质
flag=0;
return;
}
len=0;
ll l=1,r=p;
ll m;
while(l<=r){
ll mid=(l+r)>>1;
if(mid*mid>=p)
r=mid-1,m=mid;
else l=mid+1;
}
ll tmp=b%p;
for(int i=0;i<=m;i++){
len++;
_hash[len].t=tmp;
_hash[len].i=i;
tmp=tmp*a%p;
}
sort(_hash+1,_hash+1+len,comp);
tmp=power(a,m,p);
ll ret=1;
for(int i=1;i<=m;i++){
ret=ret*tmp%p;
int id=Find(ret,len);
if(id!=-1){
printf("%lld\n",m*i-id);
flag=1;
return;
}
}
}
int main(){
while(~scanf("%lld%lld%lld",&p,&a,&b)){
flag=0;
BSGS(a,b,p);
if(!flag)
puts("no solution");
}
return 0;
}
2次剩余
方程有解当且仅当$n^{\frac{p-1}{2}}\equiv 1(\mod p)$。设$\omega =a2-n$,若$x2 \equiv w(\mod p)$无解,则$x\equiv (a+\sqrt(w))^{\frac{p-1}{2}}$为解。
n=0,p=2特判。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll power(ll a,ll n,ll p){
if(!n) return 1;
ll ret=power(a,n/2,p);
ret=ret*ret%p;
if(n&1) return ret*a%p;
return ret;
}
ll Legendre(ll a,ll p){
return power(a,(p-1)>>1,p);
}
struct node{
ll p,d;
};
node mul(node A,node B,ll p,ll w){
node res;
res.p=(A.p*B.p%p+A.d*B.d%p*w%p)%p;
res.d=(A.p*B.d%p+A.d*B.p%p)%p;
return res;
}
node _power(node a,ll n,ll p,ll w){
if(!n) return node{1,0};
node ret=_power(a,n/2,p,w);
ret=mul(ret,ret,p,w);
if(n&1) return mul(ret,a,p,w);
return ret;
}
ll solve(ll n,ll p){
if(p==2) return 1;
if(Legendre(n,p)+1==p) return -1;
ll a,t;
ll w;
while(1){
a=rand()%p;
t=a*a-n;
w=(t%p+p)%p;
if(Legendre(w,p)+1==p) break;
}
node res={a,1};
res=_power(res,(p+1)>>1,p,w);
return res.p;
}
int main(){
srand((unsigned)time(0));
ll n,p;
while(~scanf("%lld%lld",&p,&n)){//x^2=n(mod p)
n %= p;
ll res=solve(n,p);
if(res==-1){
puts("No Solution");
continue;
}
ll res2=p-res;
if(res>res2) swap(res,res2);
if(res==res2)
printf("%lld\n",res);
else printf("%lld %lld\n",res,res2);
}
return 0;
}
N次剩余
求出$P$的原根$g$,设$gy=x$,$gt=a$。则$g^{y\times N}\equiv g^t(\mod p)$。
转换成$N\times y \equiv t(\mod (p-1))$。扩展欧几里得求$y$,BSGS求$t$。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<ll> c;
const int maxn = 1e5+7;
template<typename T> inline void read(T &x){
x=0;T f=1;char ch;do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');do x=x*10+ch-'0',ch=getchar();while(ch<='9'&&ch>='0');x*=f;
}
template<typename A,typename B> inline void read(A&x,B&y){read(x);read(y);}
template<typename A,typename B,typename C> inline void read(A&x,B&y,C&z){read(x);read(y);read(z);}
template<typename A,typename B,typename C,typename D> inline void read(A&x,B&y,C&z,D&w){read(x);read(y);read(z);read(w);}
inline ll power(ll a,ll n,int p){
ll ret=1;ll now=a;
while(n!=0){
if(n&1)
ret=ret*now%p;
now=now*now%p;
n>>=1;
}
return ret;
}
inline bool pan_g(ll g, int p) {
for (int i = 0; i < c.size(); ++i)
if (power(g, c[i], p) == 1)
return 0;
return 1;
}
inline ll findg(ll p) {
c.clear();
ll tmp = p - 1;
ll k = 2;
while (k * k <= tmp) {
if (tmp % k == 0) {
c.emplace_back(k);
while (tmp % k == 0)
tmp /= k;
}
++k;
}
if (tmp != 1)
c.emplace_back(tmp);
for (int i = 0; i < c.size(); ++i)
c[i] = (p-1) / c[i];
ll g = 1;
while (true) {
if (pan_g(g, p)) {
return g;
}
++g;
}
return 0;
}
struct node{
ll t,i;
};
struct Hash{
vector<node> a[maxn];
void init(){
for(int i=0;i<maxn;i++)
a[i].clear();
}
int getkey(ll t){
return t*817 % maxn;
}
void insert(ll t,ll i){
int key=getkey(t);
a[key].emplace_back(node{t,i});
}
ll Search(ll t){
int key=getkey(t);
ll mx=-1;
for(auto E:a[key]){
if(E.t==t){
mx=max(mx,E.i);
}
}
return mx;
}
}T;
inline bool comp(node A,node B){
if(A.t==B.t)
return A.i<B.i;
return A.t<B.t;
}
inline ll BSGS(ll a,ll b,int p){//a^x%p=b
if(/*gcd(a,p)!=1*/a % p==0){//互质
return -1;
}
ll l=1,r=p;
ll m;
while(l<=r){
ll mid=(l+r)>>1;
if(mid*mid>=p)
r=mid-1,m=mid;
else l=mid+1;
}
ll tmp=b%p;
T.init();
for(int i=0;i<=m;i++){
T.insert(tmp,i);
tmp=tmp*a%p;
}
tmp=power(a,m,p);
ll ret=1;
for(int i=1;i<=m;i++){
ret=ret*tmp%p;
int id=T.Search(ret);
if(id!=-1){
return m*i-id;
}
}
return -1;
}
inline ll exgcd(ll a,ll b,ll&x,ll&y){
if(!b)return x=1,y=0,a;
ll d=exgcd(b,a%b,x,y),t=x;
return x=y,y=t-a/b*y,d;
}
int The_res[1000005];
inline int solve(int p,ll n,ll a){
if(!a){
The_res[1]=0;
return 1;
}
int len=0;
ll g=findg(p);
ll m=BSGS(g,a,p);
if(m==-1) return -1;
ll A=n,B=p-1,C=m,x,y;
ll G=exgcd(A,B,x,y);
if(C%G) return -1;
x=x*(C/G)%B;
ll delta=B/G;
for(int i=0;i<G;i++){
x=((x+delta)%B+B)%B;
The_res[++len]=power(g,x,p);
}
sort(The_res+1,The_res+1+len);
return len;
}
int main(){
ll P,A,B;int T;
read(T);
while(T--){//X ^ A % P = B
read(P,A,B);
int res=solve(P,A,B);
if(res==-1){
puts("No Solution");
} else {
The_res[0]=-1;
int _=0;
for(int i=1;i<=res;i++){
if(The_res[i]==The_res[i-1]) continue;
The_res[++_]=The_res[i];
}
for(int i=1;i<_;i++)
printf("%d ",The_res[i]);
printf("%d\n",The_res[_]);
}
}
return 0;
}
3次剩余
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll p,a;//x^3=a (% p)
inline ll power(ll a,ll n,int p){
ll ret=1;ll now=a;
while(n!=0){
if(n&1)
ret=ret*now%p;
now=now*now%p;
n>>=1;
}
return ret;
}
struct node{
ll x,y,z;
node operator * (const node & t){
return node{
(x*t.x+(y*t.z+z*t.y)%p*a)%p,
(x*t.y+y*t.x+z*t.z%p*a)%p,
(x*t.z+y*t.y+z*t.x)%p
};
}
};
inline node power(node a,ll n,int p){
if(!n) return node{1,0,0};
node ret=power(a,n/2,p);
ret=ret*ret;
if(n&1){
ret=ret*a;
return ret;
}
return ret;
}
ll Legendre(ll a,ll p){
return power(a,(p-1)>>1,p);
}
struct _node{
ll p,d;
};
_node mul(_node A,_node B,ll p,ll w){
_node res;
res.p=(A.p*B.p%p+A.d*B.d%p*w%p)%p;
res.d=(A.p*B.d%p+A.d*B.p%p)%p;
return res;
}
_node _power(_node a,ll n,ll p,ll w){
if(!n) return _node{1,0};
_node ret=_power(a,n/2,p,w);
ret=mul(ret,ret,p,w);
if(n&1) return mul(ret,a,p,w);
return ret;
}
ll rando(){
return (ll)rand() << 15 | rand();
}
ll solve(ll n,ll p){
if(p==2) return 1;
if(Legendre(n,p)+1==p) return -1;
ll a,t;
ll w;
while(1){
a=rand()%p;
t=a*a-n;
w=(t%p+p)%p;
if(Legendre(w,p)+1==p) break;
}
_node res={a,1};
res=_power(res,(p+1)>>1,p,w);
return res.p;
}
int main(){
srand((unsigned)time(0));
int T;scanf("%d",&T);
while(T--){
scanf("%lld%lld",&p,&a);
if(!a || p<=3){
printf("%lld\n",a);
continue;
}
if(p%3!=1){
printf("%lld\n",power(a,(p+p-1)/3,p));
continue;
}
if(power(a,(p-1)/3,p)!=1){
puts("No Solution");
continue;
}
node ret;
while(1){
ret.x=rando()%p;
ret.y=rando()%p;
ret.z=rando()%p;
ret=power(ret,(p-1)/3,p);
if(ret.x==0&&ret.z==0) break;
}
ll res=(solve(p-3,p)-1)*((p+1)/2)%p;
ll ans[3];
ans[0]=power(ret.y,p-2,p);
ans[1]=ans[0]*res%p;
ans[2]=ans[1]*res%p;
sort(ans,ans+3);
for(int i=0;i<3;i++)
printf("%lld%c",ans[i]," \n"[i==2]);
}
return 0;
}
积性函数求和
求$\sum \phi(i)$和$\sum \mu(i)$.
令$F(n)$为$f(n)$前缀和,$G(n)$为$g(n)$前缀和,并且有$g(n)=\sum {i|n}f(i)$,那么有
$G(n)=\sum^{n}g(i)$
$= \sum_{i=1}^{n}\sum {j|i}f(j) = \sum^{n}\sum {j|i}f(j) = \sum^{n}\left \lfloor {\frac{n}{j}} \right \rfloor f(j) = \sum_{j=1}^{n}F(\left \lfloor {\frac{n}{j}} \right \rfloor)$.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 2000000;
int prime[M / 5], tot = 0, n;
ll phi[M + 5], mu[M + 5];
ll p[M + 5], q[M + 5];
bool vis[M + 5];
void init(){
int i, j;
phi[1] = mu[1] = 1;
for(i=2;i<=M;i++){
if(!phi[i]){
phi[i] = i - 1;
mu[i] = -1;
prime[++tot] = i;
}
for(j=1;j<=tot && i*prime[j]<=M;j++)
if(i % prime[j]){
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
mu[i * prime[j]] = -mu[i];
} else {
phi[i * prime[j]] = phi[i] * prime[j];
mu[i * prime[j]] = 0;
break;
}
}
for(i=2;i<=M;i++){
phi[i] += phi[i-1];
mu[i] += mu[i-1];
}
}
ll get_phi(int x){
return (x<=M)?phi[x]:p[n/x];
}
ll get_mu(int x){
return (x<=M)?mu[x]:q[n/x];
}
void solve(int x){
if(x<=M) return;
int i,j=1,t=n/x;
if(vis[t]) return;
vis[t]=1;
p[t] = ((ll)x + 1) * x >> 1, q[t] = 1;
while(j < x){
i = j + 1, j = x / ( x / i), solve(x / i);
p[t] -= get_phi(x / i) * (j - i + 1);
q[t] -= get_mu(x / i) * (j - i + 1);
}
}
int main(){
int T;
cin>>T;
init();
while(T--){
scanf("%d",&n);
memset(vis,0,sizeof vis);
if(n <= M)
printf("%lld %lld\n",phi[n],mu[n]);
else{
solve(n);
printf("%lld %lld\n",p[1],q[1]);
}
}
return 0;
}
Meisell-Lehmer
小于等于n 有多少个质数。
#include<cstdio>
#include<cmath>
using namespace std;
#define LL long long
const int N = 5e6 + 2;
bool np[N];
int prime[N], pi[N];
int getprime()
{
int cnt = 0;
np[0] = np[1] = true;
pi[0] = pi[1] = 0;
for(int i = 2; i < N; ++i)
{
if(!np[i]) prime[++cnt] = i;
pi[i] = cnt;
for(int j = 1; j <= cnt && i * prime[j] < N; ++j)
{
np[i * prime[j]] = true;
if(i % prime[j] == 0) break;
}
}
return cnt;
}
const int M = 7;
const int PM = 2 * 3 * 5 * 7 * 11 * 13 * 17;
int phi[PM + 1][M + 1], sz[M + 1];
void init()
{
getprime();
sz[0] = 1;
for(int i = 0; i <= PM; ++i) phi[i][0] = i;
for(int i = 1; i <= M; ++i)
{
sz[i] = prime[i] * sz[i - 1];
for(int j = 1; j <= PM; ++j) phi[j][i] = phi[j][i - 1] - phi[j / prime[i]][i - 1];
}
}
int sqrt2(LL x)
{
LL r = (LL)sqrt(x - 0.1);
while(r * r <= x) ++r;
return int(r - 1);
}
int sqrt3(LL x)
{
LL r = (LL)cbrt(x - 0.1);
while(r * r * r <= x) ++r;
return int(r - 1);
}
LL getphi(LL x, int s)
{
if(s == 0) return x;
if(s <= M) return phi[x % sz[s]][s] + (x / sz[s]) * phi[sz[s]][s];
if(x <= prime[s]*prime[s]) return pi[x] - s + 1;
if(x <= prime[s]*prime[s]*prime[s] && x < N)
{
int s2x = pi[sqrt2(x)];
LL ans = pi[x] - (s2x + s - 2) * (s2x - s + 1) / 2;
for(int i = s + 1; i <= s2x; ++i) ans += pi[x / prime[i]];
return ans;
}
return getphi(x, s - 1) - getphi(x / prime[s], s - 1);
}
LL getpi(LL x)
{
if(x < N) return pi[x];
LL ans = getphi(x, pi[sqrt3(x)]) + pi[sqrt3(x)] - 1;
for(int i = pi[sqrt3(x)] + 1, ed = pi[sqrt2(x)]; i <= ed; ++i) ans -= getpi(x / prime[i]) - i + 1;
return ans;
}
LL lehmer_pi(LL x)
{
if(x < N) return pi[x];
int a = (int)lehmer_pi(sqrt2(sqrt2(x)));
int b = (int)lehmer_pi(sqrt2(x));
int c = (int)lehmer_pi(sqrt3(x));
LL sum = getphi(x, a) +(LL)(b + a - 2) * (b - a + 1) / 2;
for (int i = a + 1; i <= b; i++)
{
LL w = x / prime[i];
sum -= lehmer_pi(w);
if (i > c) continue;
LL lim = lehmer_pi(sqrt2(w));
for (int j = i; j <= lim; j++) sum -= lehmer_pi(w / prime[j]) - (j - 1);
}
return sum;
}
int main()
{
init();
LL n;
while(~scanf("%lld",&n))
{
printf("%lld\n",lehmer_pi(n));
}
return 0;
}
伯努利数
B[0]=1;
for(int i=1;i<=2000;i++){
for(int j=0;j<i;j++)
B[i]=(B[i]+B[j]*c[i+1][j]%mod)%mod;
B[i]=(-B[i]*power(i+1,mod-2,mod)%mod+mod)%mod;
}
数论公式
1.$n=\sum a_{i}^{p_i}$,$\phi(n)=n\prod (1-\frac{1}{p_i})$.
2.m,n互质,则$\phi(mn)=\phi(m)\phi(n)$.
3.$n=\sum a_{i}^{p_i}$,$f(n)=\prod (1-a_i)$.
4.$$\mu(n)=\left{\begin{matrix}
1|n=1\
(-1)^k|n=p_1p_2...p_k\
0|others
\end{matrix}\right.$$
5.$\sum_{d|n}\mu(d)=[n=1]$.
数学
高斯消元
高斯消元Naive版
判断有无解,返回自由元个数。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
typedef long long LL;
const double EPS=1e-6;
const int N=55;
struct matrix{
int a[N][N];
int row,col;
matrix():row(N),col(N){memset(a,0,sizeof(a));}
matrix(int x,int y):row(x),col(y){
memset(a,0,sizeof(a));
}
int* operator [](int x){return a[x];}
void print(){
for (int i=0;i<row;i++){
for (int j=0;j<col;j++)
printf("%d ",a[i][j]);
puts("");
}
puts("");
}
};
int Gauss(matrix a,int m,int n){
int x_cnt = 0;
int col, k; //col 为列号,k 为行号
for (k=0,col=0;k<m&&col<n; ++k, ++col){
int r = k; //r 为第col 列的一个1
for (int i=k;i<m;++i) if (a[i][col])r=i;
if (!a[r][col]){ k--; continue;}
if (r!=k)for (int i=col;i<=n;++i)
swap( a[r][i], a[k][i]);
for (int i=k+1;i<m; ++i)if (a[i][col])//消元
for (int j=col;j<=n;++j)a[i][j]^=a[k][j];
}
for (int i=k;i<m;++i) if (a[i][n])return -1;
if (k<=n)return n-k; //返回自由元个数
}
高斯消元Mid版
求出一组解。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 1010;
const double EPS=1e-7;
int m,n;
double a[N][N],x[N];
int Gauss(int m,int n){
int col=0, k=0;//col 为列号,k 为行号
for (;k<m&&col<n;++k,++col){
int r = k;
for (int i=k+1;i<m;++i)
if(fabs(a[i][col])>fabs(a[r][col]))r=i;
if (fabs(a[r][col])<EPS){k--;continue;}//列全为0
if (r!=k)for(int i=col;i<=n;++i)
swap(a[k][i],a[r][i]);
for (int i=k+1;i<m;++i){//消元
double t = a[i][col]/a[k][col];
for (int j=col;j<=n;j++)a[i][j]-=a[k][j]*t;
a[i][col] = 0;
}
}
for(int i=k ;i<m ;++i)//无解
if (fabs(a[i][n])>EPS) return -1;
if (k < n) return n - k; //自由元个数
for (int i =n-1; i>=0; i--){//回带求解
double temp = a[i][n];
for (int j=i+1; j<n; ++j)
temp -= x[j] * a[i][j];
x[i] = (temp / a[i][i]);
}
return 0;
}
组合数
1.$O(n^2)$算法——杨辉三角。
2.$O(n)$算法——阶乘取模 + 乘法逆元。
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<maxn;i++){
fac[i]=fac[i-1]*i%mod;
}
inv[maxn-1]=power(fac[maxn-1],mod-2);
for(int i=maxn-1;i>=1;i--)
inv[i-1]=inv[i]*i%mod;
}
ll C(ll n,ll m){
return fac[n]*inv[n-m]%mod*inv[m]%mod;
}
3.$C(n,m)=\frac{n!}{m!(n - m)!}$.
如果p是质数,直接费马小定理求逆元。
LL C(LL n, LL m){
if(m > n) return 0;
LL ans = 1;
for(int i = 1; i <= m; i++){
LL a = (n + i - m) % MOD;
LL b = i % MOD;
ans = ans * (a * quick_mod(b, p-2) % MOD) % MOD;
}
return ans;
}
4.Lucas定理:P很小的情况下。
$C(n, m) % p = C(n / p, m / p) * C(n% p, m% p) % p$
ll Lucas(ll n, ll m){
if(m == 0) return 1;
return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
void InitFac(){//阶乘预处理
fac[0] = 1;
for(int i=1; i<=n; i++)
fac[i] = (fac[i-1] * i) % MOD;
}
ll C(ll n,ll m,ll p,ll fac[]){
if(n < m) return 0;
return fac[n] * quick_mod(fac[m] * fac[n-m], p - 2, p) % p;
}
5.扩展lucas,求C(n,m)%(p1^k1 p2^k2 ... pn^kn)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x){
x=0;T f=1;char ch;do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');do x=x*10+ch-'0',ch=getchar();while(ch<='9'&&ch>='0');x*=f;
}
template<typename A,typename B> inline void read(A&x,B&y){read(x);read(y);}
template<typename A,typename B,typename C> inline void read(A&x,B&y,C&z){read(x);read(y);read(z);}
template<typename A,typename B,typename C,typename D> inline void read(A&x,B&y,C&z,D&w){read(x);read(y);read(z);read(w);}
ll mod;
ll n,m;
ll w[1005];
inline ll power(ll a,ll n,ll p){
ll ret=1;ll now=a;
while(n!=0){
if(n&1)
ret=ret*now%p;
now=now*now%p;
n>>=1;
}
return ret;
}
ll exgcd(ll a,ll b,ll&x,ll&y){
if(!b)return x=1,y=0,a;
ll d=exgcd(b,a%b,x,y),t=x;
return x=y,y=t-a/b*y,d;
}
ll inv(ll A,ll mod){
if (!A) return 0ll;
ll a=A,b=mod,x=0ll,y=0ll;
exgcd(a,b,x,y);
x=(x%b+b)%b;
if(!x) x+=b;
return x;
}
ll mul(ll n,ll pi,ll pk){
if (!n) return 1ll;
ll ans=1ll;
for(ll i=2;i<=pk;++i)
if(i%pi) ans=ans*i%pk;
ans=power(ans,n/pk,pk);
for(ll i=2;i<=n%pk;++i)
if(i%pi) ans=ans*i%pk;
return ans*mul(n/pi,pi,pk)%pk;
}
ll C(ll n,ll m,ll mod,ll pi,ll pk){
if(m>n) return 0ll;
ll a=mul(n,pi,pk),b=mul(m,pi,pk),c=mul(n-m,pi,pk);
ll k=0,res;
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;
res=a*inv(b,pk)%pk*inv(c,pk)%pk*power(pi,k,pk)%pk;
return res*(mod/pk)%mod*inv(mod/pk,pk)%mod;
}
int main(){
//freopen("in.txt","r",stdin);
read(mod);
read(n,m);
ll sum=0;
for(int i=1;i<=m;i++) read(w[i]),sum+=w[i];
if(n<sum){
puts("Impossible");
} else {
ll ans=1ll;
for(int j=1;j<=m;j++){//C(n,m)%(p1^k1 p2^k2 ... pn^kn)
n-=w[j-1];
ll P=mod;
ll now=0ll;
for(ll i=2;i*i<=P;i++){
if(P%i==0){
ll pk=1ll;
while(P%i==0) pk*=i,P/=i;
now=(now+C(n,w[j],mod,i,pk))%mod;
}
}
if(P>1)
now=(now+C(n,w[j],mod,P,P))%mod;
ans=ans*now%mod;
}
cout<<ans<<endl;
}
return 0;
}
6.(PS):组合数奇偶性:$C_{n}^{m} \mod 2 \equiv [(n& m) == m]$.
威尔逊定理
$(p-1)!\equiv -1(\mod p)$.
$f_n = {(-1)}^{\frac{n}{p}} * f_{(n\mod p)} * f_{\frac{n}{p}}$.
pair<int, int> facn(int n, int tp, int Mod) {//fac(n) = (-1) ^ (n / p) * (n % p)! * fac(n / p)
if (!n) return make_pair(1, 0);
int tmp = n / Mod;
pair<int, int> tt = facn(tmp, tp, Mod);
return make_pair(power(fac[tp][Mod - 1], tmp, Mod) * fac[tp][n % Mod] % Mod * tt.first % Mod, tt.second + tmp);
}
卡特兰数
$f(n)=\frac{C_{2n}{n}}{(n+1)}=\textrm{C}_{2n}-\textrm{C}_{2n}^{n-1}$
错位排序
D[1] = 0; D[2] = 1;
for(int i = 3; i < 25; i++) {
D[i] = (i - 1) * (D[i - 1] + D[i - 2]);
}
第一类斯特林数
p个不同人围k个相同圆桌而坐,要求各桌非空,其不同方案数为第一类Stirling数s[p][k]。
s[p][p]=1,s[p][0]=0,s[p][k]=(p-1)s[p-1][k]+s[p-1][k-1]。
stir[1][0]=0;
stir[1][1]=1;
for(int i=2;i<=20;i++)
for(int j=1;j<=i;j++)
stir[i][j]=stir[i-1][j-1]+(i-1)*stir[i-1][j];
第二类斯特林数
s[n][k]表示把从1到n标号的n个球放到k个无区别的盒子里,要求每个盒子里至少有一个小球不同的放法数量。
memset(s,0,sizeof s);
s[0][0]=1;
for(int i=1;i<=2000;i++)
for(int j=1;j<=2000;j++)
s[i][j] = (s[i - 1][j] * j + s[i - 1][j - 1]) % mod;
容斥原理
获取容斥系数.
void init(int n){
top=0;
memset(p,0,sizeof p);
for(int i=2;i<=n;i++){
if(!p[i]){
prime[++top]=i;
}
for(int j=1;j<=top && i*prime[j]<=n;j++){
p[i*prime[j]]=1;
if(i%prime[j]==0){
break;
}
}
}
for(int i=1;i<=top;i++){
for(int j=prime[i];j<=n;j+=prime[i]){
int t=f[j].size();
for(int k=0;k<t;k++){
f[j].push_back(f[j][k]*prime[i]);
g[j].push_back(-g[j][k]);
}
f[j].push_back(prime[i]);
g[j].push_back(1);
}
}
}
矩阵快速幂
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int P = 1e9+7;
const int N=4;
ll n,m;
struct matrix{//加maxsize 动态边界加速
ll a[N][N];
int row,col;
matrix():row(N),col(N){memset(a,0,sizeof(a));}
matrix(int x,int y):row(x),col(y){memset(a,0,sizeof(a));}
ll* operator [] (int x){return a[x];}
matrix operator * (matrix x){
matrix tmp ;
for (int i=0;i<N;i++)
for (int j=0;j<N;j++){
tmp[i][j]=0;
for (int k=0;k<N;k++)
tmp[i][j]=(tmp[i][j]+a[i][k]*x[k][j])%P;
}
return tmp;
}
void operator *= (matrix x){*this = *this * x;}
matrix operator ^ (ll x){
matrix ret;
for (int i=0;i<N;i++)ret[i][i]=1;
matrix tmp = *this;
for (;x;x>>=1,tmp*=tmp){if(x&1)ret *=tmp;}
return ret;
}
void print(){
for (int i=0;i<N;i++){
for (int j=0;j<N;j++)
printf("%d ",a[i][j]);
puts("");
}
}
};
ll pow(ll a,ll n){
if(n==0) return 1ll;
ll tmp = pow(a,n/2);
if(n&1) return tmp*tmp%P*a%P;
return tmp*tmp%P;
}
线性递推式
杜教的线性递推式板子。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <cassert>
using namespace std;
#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=1000000007;
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;}
// head
int _;
ll n;
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,ll 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) {
ll ans=0,pnt=0;
ll 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() {
for (scanf("%d",&_);_;_--) {
scanf("%lld",&n);
printf("%d\n",linear_seq::gao(VI{1,1,2,3,5},n)); // 预处理生成矩阵大小两倍
}
return 0;
}
FFT
#include <bits/stdc++.h>
using namespace std;
struct comp {
double r,i;
comp(double _r=0,double _i=0) {
r=_r;
i=_i;
}
comp operator+(const comp x) {
return comp(r+x.r,i+x.i);
}
comp operator-(const comp x) {
return comp(r-x.r,i-x.i);
}
comp operator*(const comp x) {
return comp(r*x.r-i*x.i,r*x.i+i*x.r);
}
};
const double pi=acos(-1.0);
void FFT(comp a[],int n,int t) {
for(int i=1,j=0; i<n-1; i++) {
for(int s=n; j^=s>>=1,~j&s;);
if(i<j)swap(a[i],a[j]);
}
for(int d=0; (1<<d)<n; d++) {
int m=1<<d,m2=m<<1;
double o=pi/m*t;
comp _w(cos(o),sin(o));
for(int i=0; i<n; i+=m2) {
comp w(1,0);
for(int j=0; j<m; j++) {
comp &A=a[i+j+m],&B=a[i+j],t=w*A;
A=B-t;
B=B+t;
w=w*_w;
}
}
}
if(t==-1)for(int i=0; i<n; i++)a[i].r/=n;
}
char a[5005],b[5005];
comp x[5005],y[5005];
int i,j;
int sum[50000];
int main(){
while(~scanf("%s%s",a,b)){
int len1=strlen(a);
int len2=strlen(b);
int l=1;
while(l<len1*2 || l<len2*2) l<<=1;
for(i=0;i<len1;i++){
x[i].r=a[len1-i-1]-'0';
x[i].i=0.0;
}
for(;i<l;i++)
x[i].r=0.0,x[i].i=0.0;
for(i=0;i<len2;i++){
y[i].r=b[len2-i-1]-'0';
y[i].i=0.0;
}
for(;i<l;i++)
y[i].r=0.0,y[i].i=0.0;
FFT(x,l,1);FFT(y,l,1);
for(i=0;i<l;i++)
x[i]=x[i]*y[i];
FFT(x,l,-1);
for(i=0;i<l;i++) sum[i]=x[i].r+0.5;
for(i=0;i<l;i++){
sum[i+1]+=sum[i]/10;
sum[i]%=10;
}
l=len1+len2-1;
while(sum[l]==0 && l>0) --l;
for(i=l;i>=0;i--) putchar(sum[i]+'0');
puts("");
}
return 0;
}
计算几何
计算几何板子
#include <bits/stdc++.h>
using namespace std;
const double eps=1e-9;
int sign(double k){
if (k>eps) return 1; else if (k<-eps) return -1; return 0;
}
int cmp(double k1,double k2){return sign(k1-k2);}
//k3在[k1,k2]内
int inmid(double k1,double k2,double k3){return sign(k1-k3)*sign(k2-k3)<=0;}
struct point{
double x,y;
point operator + (point k1){return (point){k1.x+x,k1.y+y};}
point operator - (point k1){return (point){x-k1.x,y-k1.y};}
point operator * (double k1){return (point){x*k1,y*k1};}
point operator / (double k1){return (point){x/k1,y/k1};}
//逆时针旋转
point turn(double k1){return (point){x*cos(k1)-y*sin(k1),x*sin(k1)+y*cos(k1)};}
point turn90(){return (point){-y,x};}
bool operator < (point k1){
int a=cmp(x,k1.x);
if (a==-1) return 1; else if (a==1) return 0; else return cmp(y,k1.y)==-1;
}
double abs(){return sqrt(x*x+y*y);}
double abs2(){return x*x+y*y;}
double dis(point k1){return ((*this)-k1).abs();}
point unit(){double w=abs(); return (point){x/w,y/w};}
void scan(){scanf("%lf%lf",&x,&y);}
void print(){printf("%.11lf %.11lf\n",x,y);}
double getw(){return atan2(y,x);}
point getdel(){if (sign(x)==-1||(sign(x)==0&&sign(y)==-1)) return (*this)*(-1); else return (*this);}
};
int inmid(point k1,point k2,point k3){
return inmid(k1.x,k2.x,k3.x)&&inmid(k1.y,k2.y,k3.y);
}
double cross(point k1,point k2){
return k1.x*k2.y-k1.y*k2.x;
}
double dot(point k1,point k2){
return k1.x*k2.x+k1.y*k2.y;
}
double rad(point k1,point k2){
return atan2(cross(k1,k2),dot(k1,k2));
}
point proj(point k1,point k2,point q){
//q到直线k1,k2的投影
point k=k2-k1;
return k1+k*(dot(q-k1,k)/k.abs2());
}
point reflect(point k1,point k2,point q){
return proj(k1,k2,q)*2-q;
}
int clockwise(point k1,point k2,point k3){
//k1 k2 k3 逆时针 1 顺时针 -1 否则 0
return sign(cross(k2-k1,k3-k1));
}
//求直线(L)线段(S)k1,k2和k3,k4的交点
int checkLL(point k1,point k2,point k3,point k4){
return cmp(cross(k3-k1,k4-k1),cross(k3-k2,k4-k2))!=0;
}
point getLL(point k1,point k2,point k3,point k4){
double w1=cross(k1-k3,k4-k3),w2=cross(k4-k3,k2-k3);
return (k1*w2+k2*w1)/(w1+w2);
}
int intersect(double l1,double r1,double l2,double r2){
if (l1>r1) swap(l1,r1); if (l2>r2) swap(l2,r2);
return cmp(r1,l2)!=-1&&cmp(r2,l1)!=-1;
}
int checkSS(point k1,point k2,point k3,point k4){
return intersect(k1.x,k2.x,k3.x,k4.x)&&intersect(k1.y,k2.y,k3.y,k4.y)&&
sign(cross(k3-k1,k4-k1))*sign(cross(k3-k2,k4-k2))<=0&&
sign(cross(k1-k3,k2-k3))*sign(cross(k1-k4,k2-k4))<=0;
}
double disSP(point k1,point k2,point q){
point k3=proj(k1,k2,q);
if (inmid(k1,k2,k3)) return q.dis(k3); else return min(q.dis(k1),q.dis(k2));
}
double disSS(point k1,point k2,point k3,point k4){
if (checkSS(k1,k2,k3,k4)) return 0;
else return min(min(disSP(k1,k2,k3),disSP(k1,k2,k4)),min(disSP(k3,k4,k1),disSP(k3,k4,k2)));
}
int onS(point k1,point k2,point q){
return inmid(k1,k2,q)&&sign(cross(k1-q,k2-k1))==0;
}
//最近点对,先要按照x坐标排序
double closepoint(vector<point>&A,int l,int r){
if (r-l<=5){
double ans=1e20;
for (int i=l;i<=r;i++) for (int j=i+1;j<=r;j++) ans=min(ans,A[i].dis(A[j]));
return ans;
}
int mid=l+r>>1; double ans=min(closepoint(A,l,mid),closepoint(A,mid+1,r));
vector<point>B; for (int i=l;i<=r;i++) if (abs(A[i].x-A[mid].x)<=ans) B.push_back(A[i]);
sort(B.begin(),B.end(),[](point k1,point k2){return k1.y<k2.y;});
for (int i=0;i<B.size();i++) for (int j=i+1;j<B.size()&&B[j].y-B[i].y<ans;j++) ans=min(ans,B[i].dis(B[j]));
return ans;
}
//多边形用 vector<point> 表示,逆时针
double area(vector<point> A){
double ans=0;
for (int i=0;i<A.size();i++) ans+=cross(A[i],A[(i+1)%A.size()]);
return ans/2;
}
int checkconvex(vector<point>A){
int n=A.size(); A.push_back(A[0]); A.push_back(A[1]);
for (int i=0;i<n;i++) if (sign(cross(A[i+1]-A[i],A[i+2]-A[i]))==-1) return 0;
return 1;
}
int contain(vector<point>A,point q){
//2 内部 1 边界 0 外部
A.push_back(A[0]); int pd=0;
for (int i=1;i<A.size();i++){
point u=A[i-1],v=A[i];
if (onS(u,v,q)) return 1;
if (cmp(u.y,v.y)>0) swap(u,v);
if (cmp(u.y,q.y)>=0||cmp(v.y,q.y)<0) continue;
if (sign(cross(u-v,q-v))<0) pd^=1;
}
return pd<<1;
}
vector<point> ConvexHull(vector<point>A,int flag=1){
//flag=0 不严格 flag=1 严格
int n=A.size(); vector<point>ans(n*2);
sort(A.begin(),A.end()); int now=-1;
for (int i=0;i<A.size();i++){
while (now>0&&sign(cross(ans[now]-ans[now-1],A[i]-ans[now-1]))<flag) now--;
ans[++now]=A[i];
}
int pre=now;
for (int i=n-2;i>=0;i--){
while (now>pre&&sign(cross(ans[now]-ans[now-1],A[i]-ans[now-1]))<flag) now--;
ans[++now]=A[i];
}
ans.resize(now); return ans;
}
double convexDiameter(vector<point>A){
int now=0,n=A.size(); double ans=0;
for (int i=0;i<A.size();i++){
now=max(now,i);
while (1){
double k1=A[i].dis(A[now%n]),k2=A[i].dis(A[(now+1)%n]);
ans=max(ans,max(k1,k2));
if (k2>k1) now++; else break;
}
}
return ans;
}
vector<point> convexcut(vector<point>A,point k1,point k2){
//保留 k1,k2,p 逆时针的所有点
int n=A.size(); A.push_back(A[0]); vector<point>ans;
for (int i=0;i<n;i++){
int w1=clockwise(k1,k2,A[i]),w2=clockwise(k1,k2,A[i+1]);
if (w1>=0) ans.push_back(A[i]);
if (w1*w2<0) ans.push_back(getLL(k1,k2,A[i],A[i+1]));
}
return ans;
}
struct circle{
point o;
double r;
void scan(){o.scan(); scanf("%lf",&r);}
int inside(point k){return cmp(r,o.dis(k));}
};
int checkposCC(circle k1,circle k2){
//返回两个圆的公切线数量
if (cmp(k1.r,k2.r)==-1) swap(k1,k2);
double dis=k1.o.dis(k2.o); int w1=cmp(dis,k1.r+k2.r),w2=cmp(dis,k1.r-k2.r);
if (w1>0) return 4; else if (w1==0) return 3; else if (w2>0) return 2;
else if (w2==0) return 1; else return 0;
}
vector<point> getCL(circle k1,point k2,point k3){
//沿着k2->k3方向给出,相切给出两个
point k=proj(k2,k3,k1.o); double d=k1.r*k1.r-(k-k1.o).abs2();
if (sign(d)==-1) return {};
point del=(k3-k2).unit()*sqrt(max(0.0,d));
return {k-del,k+del};
}
vector<point> getCC(circle k1,circle k2){
//沿圆k1逆时针给出,相切给出两个
int pd=checkposCC(k1,k2);
if (pd==0||pd==4) return {};
double a=(k2.o-k1.o).abs2(),cosA=(k1.r*k1.r+a-k2.r*k2.r)/(2*k1.r*sqrt(max(a,0.0)));
double b=k1.r*cosA,c=sqrt(max(0.0,k1.r*k1.r-b*b));
point k=(k2.o-k1.o).unit(),m=k1.o+k*b,del=k.turn90()*c;
return {m-del,m+del};
}
vector<point> TangentCP(circle k1,point k2){
//沿圆k1逆时针给出
double a=(k2-k1.o).abs(),b=k1.r*k1.r/a,c=sqrt(max(0.0,k1.r*k1.r-b*b));
point k=(k2-k1.o).unit(),m=k1.o+k*b,del=k.turn90()*c;
return {m-del,m+del};
}
struct line{
//p[0]->p[1]
point p[2];
line(point k1,point k2){p[0]=k1; p[1]=k2;}
line(){}
point& operator [] (int k){return p[k];}
line push(){ //向外(左手边)平移eps
const double eps = 1e-6;
point delta=(p[1]-p[0]).turn90().unit()*eps;
return {p[0]-delta,p[1]-delta};
}
};
int clockwise(line k1,point k2){
return clockwise(k1[0],k1[1],k2);
}
vector<line> TangentoutCC(circle k1,circle k2){
int pd=checkposCC(k1,k2); if (pd==0) return {};
if (pd==1){point k=getCC(k1,k2)[0]; return {(line){k,k}};}
if (cmp(k1.r,k2.r)==0){
point del=(k2.o-k1.o).unit().turn90().getdel();
return {(line){k1.o-del*k1.r,k2.o-del*k2.r},(line){k1.o+del*k1.r,k2.o+del*k2.r}};
} else {
point p=(k2.o*k1.r-k1.o*k2.r)/(k1.r-k2.r);
vector<point>A=TangentCP(k1,p),B=TangentCP(k2,p);
vector<line>ans; for (int i=0;i<A.size();i++) ans.push_back((line){A[i],B[i]});
return ans;
}
}
vector<line> TangentinCC(circle k1,circle k2){
int pd=checkposCC(k1,k2); if (pd<=2) return {};
if (pd==3){point k=getCC(k1,k2)[0]; return {(line){k,k}};}
point p=(k2.o*k1.r+k1.o*k2.r)/(k1.r+k2.r);
vector<point>A=TangentCP(k1,p),B=TangentCP(k2,p);
vector<line>ans; for (int i=0;i<A.size();i++) ans.push_back((line){A[i],B[i]});
return ans;
}
vector<line> TangentCC(circle k1,circle k2){
int flag=0; if (k1.r<k2.r) swap(k1,k2),flag=1;
vector<line>A=TangentoutCC(k1,k2),B=TangentinCC(k1,k2);
for (line k:B) A.push_back(k);
if (flag) for (line &k:A) swap(k[0],k[1]);
return A;
}
double getarea(circle k1,point k2,point k3){
//圆k1与三角形k2 k3 k1.o的有向面积交
int pd1=k1.inside(k2),pd2=k1.inside(k3);
vector<point>A=getCL(k1,k2,k3);
if (pd1>=0){
if (pd2>=0) return cross(k2-k1.o,k3-k1.o)/2;
return k1.r*k1.r*rad(A[1],k3)/2+cross(k2-k1.o,A[1]-k1.o)/2;
} else if (pd2>=0){
return k1.r*k1.r*rad(k2,A[0])/2+cross(A[0]-k1.o,k3-k1.o)/2;
}else {
int pd=cmp(k1.r,disSP(k2,k3,k1.o));
if (pd<=0) return k1.r*k1.r*rad(k2,k3)/2;
return cross(A[0]-k1.o,A[1]-k1.o)/2+k1.r*k1.r*(rad(k2,A[0])+rad(A[1],k3))/2;
}
}
公式
1.点到直线距离:$\frac{Ax_0+By_0+C}{\sqrt{A2+B2}}$.
2.两平行线距离:$\frac{|C_1-C_2|}{\sqrt{A2+B2}}$.
3.两直线夹角正切;$|\frac{A_1B_2-A_2B_1}{A_1A_2+B_1B_2}|$.
4.方向线段关于直线的反射线段:$(x-2a\frac{ax+by+c}{a2+b2},y-2b\frac{ax+by+c}{a2+b2})$,
镜面:$ax+by+c=0$.
图论
最小生成树
Kruskal
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef pair<int, int> P;
const int N = 100005;
struct Edge{
int from,to,w;
Edge(){}
Edge(int x,int y,int z):from(x),to(y),w(z){}
bool operator < (const Edge& a) const{return w > a.w;}
} edges[N * 2];
int f[N];
int F(int x){return f[x]==x ? x : (f[x]=F(f[x]));}
bool vis[N];
P kruskal(int n, int m){
int treenum = n; //forests
for (int i = 1;i <= n; i++) f[i] = i;
sort(edges, edges + m);
int cnt = 0, ans = 0;
for (int i = 0; i < m; i++){
Edge &e = edges[i];
if (F(e.from) == F(e.to)) continue;
f[F(e.from)] = F(e.to);
treenum--;
ans += e.w;
}
return P(treenum, ans);
}
int main(){
//freopen("in.txt", "r", stdin);
int n, x, y, u, v, c, m;
for (; scanf("%d%d", &n, &m)==2;){
for (int i = 1; i <= n; i++) scanf("%d%d", &x, &y);
int all = 0;
for (int i = 0; i < m; i++){
scanf("%d%d%d", &u, &v, &c);
edges[i] = Edge(u, v, c);
all += c;
}
P p = kruskal(n, m);
printf("%d %d\n", m-(n-p.fi), all - p.se);
}
return 0;
}
Prim
#include<bits/stdc++.h>
#define maxn 105
#define inf 0x3f3f3f3f
using namespace std;
int g[maxn][maxn];
int lowcost[maxn];
int visit[maxn];
int n;
int prime(){
int Min,mincost=0,next;
memset(visit,0,sizeof(visit));
for(int i=1;i<=n;i++){
lowcost[i]=g[1][i];
}
visit[1]=1;
for(int i=1;i<n;i++){
Min=inf;
for(int j=1;j<=n;j++){
if(!visit[j] && Min>lowcost[j]){
Min=lowcost[j];
next=j;
}
}
mincost+=Min;
visit[next]=1;
for(int j=1;j<=n;j++){
if(!visit[j] && lowcost[j]>g[next][j]){
lowcost[j]=g[next][j];
}
}
}
return mincost;
}
int main(){
// freopen("input.txt","r",stdin);
ios::sync_with_stdio(false);
cin.tie(0);
while(cin>>n){
memset(g,inf,sizeof(g));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>g[i][j];
}
}
int cost=prime();
cout<<cost<<endl;
}
return 0;
}
次小生成树
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
#define N 510
int map[N][N], lowcost[N], pre[N], max1[N][N], stack[N];
bool visit[N];
int n, m, sum;
void prim(){ //默认1在MST中
int temp, k;
int top; //保存最小生成树的结点
memset(visit, false, sizeof(visit)); //初始化
visit[1] = true;
sum = top = 0;
for(int i = 1; i <= n; ++i){
pre[i] = 1;
lowcost[i] = map[1][i];
}
lowcost[1] = 0;
stack[top++] = 1; //保存MST的结点
for(int i = 1; i <= n; ++i){
temp = INT_MAX;
for(int j = 1; j <= n; ++j)
if(!visit[j] && temp > lowcost[j])
temp = lowcost[k = j];
if(temp == INT_MAX) break;
visit[k] = true;
sum += temp;
for(int j = 0; j < top; ++j) //新加入点到MST各点路径最大值
max1[stack[j]][k] = max1[k][stack[j]] = max(max1[stack[j]][pre[k]], temp);
stack[top++] = k; //保存MST的结点
for(int j = 1; j <= n; ++j) //更新
if(!visit[j] && lowcost[j] > map[k][j]){
lowcost[j] = map[k][j];
pre[j] = k; //记录直接前驱
}
}
}
int main(){
int ncase, start, end, cost, minn;
scanf("%d", &ncase);
while(ncase--) {
for(int i = 1; i < N; ++i) //初始化不为0,1必须用循环。。。。
for(int j = 1; j < N; ++j){
map[i][j] = INT_MAX;
max1[i][j] = 0;
}
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i){
scanf("%d%d%d", &start, &end, &cost);
//if(cost < map[start][end])(POJ竟然出现重边的时候不选择最小的~~~)
map[start][end] = map[end][start] = cost;
}
prim();
minn = INT_MAX;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
if(i != j && i != pre[j] && j != pre[i]) //枚举MST以外的边
minn = min(minn, map[i][j] - max1[i][j]); //求出{MST外加入边-MST环上权值最大边}最小值
if(minn != 0) printf("No\n");
else printf("Yes\n");
}
return 0;
}
最短路
Dijkstra
//最短路dj
const int INF = 1000000000;
const int maxn = 500 + 10;
struct Edge {
int from, to, dist;
};
struct HeapNode {
int d, u;
bool operator < (const HeapNode& rhs) const {
return d > rhs.d;
}
};
struct Dijkstra {
int n, m;
vector<Edge> edges;
vector<int> G[maxn];
bool done[maxn]; // 是否已永久标号
int d[maxn]; // s 到各个点的距离
int p[maxn]; // 最短路中的上一条弧
void init(int n) {
this->n = n;
for(int i = 0; i < n; i++) G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int dist) {
edges.push_back((Edge){from, to, dist});
m = edges.size();
G[from].push_back(m-1);
}
void dijkstra(int s) {
priority_queue<HeapNode> Q;
for(int i = 0; i < n; i++) d[i] = INF;
d[s] = 0;
memset(done, 0, sizeof(done));
Q.push((HeapNode){0, s});
while(!Q.empty()) {
HeapNode x = Q.top(); Q.pop();
int u = x.u;
if(done[u]) continue;
done[u] = true;
for(int i = 0; i < G[u].size(); i++) {
Edge& e = edges[G[u][i]];
if(d[e.to] > d[u] + e.dist) {
d[e.to] = d[u] + e.dist;
p[e.to] = G[u][i];
Q.push((HeapNode){d[e.to], e.to});
}
}
}
}
// dist[i] 为s 到i 的距离,paths[i] 为s 到i 的最短路径(经过的结点列表,包括s )
void GetShortestPaths(int s, int* dist, vector<int>* paths) {
dijkstra(s);
for(int i = 0; i < n; i++) {
dist[i] = d[i];
paths[i].clear();
int t = i;
paths[i].push_back(t);
while(t != s) {
paths[i].push_back(edges[p[t]].from);
t = edges[p[t]].from;
}
reverse(paths[i].begin(), paths[i].end());
}
}
};
Dijkstra solver;
dijkstrafast
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
#define pa pair<ll,int>
#define INF 9000000000000000000LL
#define mp(a,b) make_pair(a,b)
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
const int maxn = 1000005;
const int maxm = 10000005;
template<typename T> inline void read(T &x) {
x = 0; T f = 1; char ch; do {ch = getchar(); if (ch == '-')f = -1;} while (ch < '0' || ch > '9'); do x = x * 10 + ch - '0', ch = getchar(); while (ch <= '9' && ch >= '0'); x *= f;
}
template<typename A, typename B> inline void read(A&x, B&y) {read(x); read(y);}
template<typename A, typename B, typename C> inline void read(A&x, B&y, C&z) {read(x); read(y); read(z);}
template<typename A, typename B, typename C, typename D> inline void read(A&x, B&y, C&z, D&w) {read(x); read(y); read(z); read(w);}
typedef __gnu_pbds::priority_queue<pa, greater<pa>, pairing_heap_tag > heap;
int n, m, cnt, head[maxn];
int T, rxa, rxc, rya, ryc, rp;
heap::point_iterator id[maxn];
int x, y, z;
ll dist[maxn];
struct data {
int to, next, v;
} e[maxm];
void init() {
memset(head, 0, sizeof head);
cnt = 0;
}
inline void insert(int u, int v, int w) {
e[++cnt].to = v;
e[cnt].next = head[u];
e[cnt].v = w;
head[u] = cnt;
}
void dijkstra() {
heap Q;
for (int i = 1; i <= n; i++)dist[i] = INF;
dist[1] = 0; id[1] = Q.push(mp(0, 1));
while (!Q.empty()) {
int now = Q.top().second; Q.pop();
for (int i = head[now]; i; i = e[i].next)
if (e[i].v + dist[now] < dist[e[i].to]) {
dist[e[i].to] = e[i].v + dist[now];
if (id[e[i].to] != 0)
Q.modify(id[e[i].to], mp(dist[e[i].to], e[i].to));
else id[e[i].to] = Q.push(mp(dist[e[i].to], e[i].to));
}
}
}
int main() {
init();
read(n, m);
read(T); read(rxa, rxc, rya, ryc); read(rp);
int a, b;
for (int i = 1; i <= T; i++) {
x = ((ll)x * rxa + rxc) % rp;
y = ((ll)y * rya + ryc) % rp;
a = min(x % n + 1, y % n + 1);
//b = max(y % n + 1, y % n + 1);
b = y % n + 1;
insert(a, b, 100000000 - 100 * a);
}
for (int i = 1; i <= m - T; i++) {
read(x, y, z);
insert(x, y, z);
}
dijkstra();
printf("%lld", dist[n]);
return 0;
}
SPFA
const int MAXN=1010;
const int INF=0x3f3f3f3f;
int t,n;
struct Edge {
int v;
int cost;
Edge(int _v=0,int _cost=0):v(_v),cost(_cost) {}
};
vector<Edge>E[MAXN];
void addedge(int u,int v,int w) {
E[u].push_back(Edge(v,w));
}
bool vis[MAXN];//在队列标志
int cnt[MAXN];//每个点的入队列次数
int dist[MAXN];
bool SPFA(int start,int n) {
memset (vis,false,sizeof(vis));
for(int i=1; i<=n; i++)dist[i]=INF;
vis[start]=true;
dist[start]=0;
queue<int>que;
while(!que.empty())que.pop();
que.push(start);
memset (cnt,0,sizeof(cnt));
cnt[start]=1;
while(!que.empty()) {
int u=que.front();
que.pop();
vis[u]=false;
for(int i=0; i<E[u].size(); i++) {
int v=E[u][i].v;
if(dist[v]>dist[u]+E[u][i].cost) {
dist[v]=dist[u]+E[u][i].cost;
if(!vis[v]) {
vis[v]=true;
que.push(v);
if(++cnt[v]>n)return false;
//cnt[i] 为入队列次数,用来判定是否存在负环回路
}
}
}
}
return true;
}
void init(int x) {
for(int i=1;i<=x;i++) E[i].clear();
}
SPFA判负环
struct Edge {
int from,to,dist;
};
struct BellmanFord {
int n, m;
vector<Edge> edges;
vector<int> G[maxn];
bool inq[maxn]; // 是否在队列中
int d[maxn]; // s 到各个点的距离
int p[maxn]; // 最短路中的上一条弧
int cnt[maxn]; // 进队次数
void init(int n) {
this->n = n;
for(int i = 0; i < n; i++) G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int dist) {
edges.push_back((Edge){from, to, dist});
m = edges.size();
G[from].push_back(m-1);
}
bool negativeCycle() {
queue<int> Q;
memset(inq, 0, sizeof(inq));
memset(cnt, 0, sizeof(cnt));
for(int i = 0; i < n; i++) { d[i] = inf; inq[i] = true; Q.push(i); }
while(!Q.empty()) {
int u = Q.front(); Q.pop();
inq[u] = false;
for(int i = 0; i < G[u].size(); i++) {
Edge& e = edges[G[u][i]];
if(d[e.to] > d[u] + e.dist) {
d[e.to] = d[u] + e.dist;
p[e.to] = G[u][i];
if(!inq[e.to]) { Q.push(e.to); inq[e.to] = true; if(++cnt[e.to] > n) return true; }
}
}
}
return false;
}
}sp;
次短路
#include<bits/stdc++.h>
#define maxn 100050
#define LL long long
#define P pair<LL,int>
#define mm(a,b) memset(a,b,sizeof(a))
using namespace std;
const LL INF =(LL)1e18;
struct node{
int to;
LL w;
node(int to,LL w):to(to),w(w){}
};
vector<node>G[maxn];
bool vis[maxn];
LL d[maxn],d1[maxn];
int n,m;
void solve(){
priority_queue<P,vector<P>,greater<P> >que;
fill(d+1,d+1+n,INF);
fill(d1+1,d1+1+n,INF);
d[1]=0;
que.push(P(0,1));
while(!que.empty()){
P p=que.top();
que.pop();
int v=p.second;
if(d1[v]<p.first) continue;
int len=G[v].size();
for(int i=0;i<len;i++){
node &e=G[v][i];
LL d2=p.first+e.w;
if(d[e.to]>d2){
swap(d[e.to],d2);
que.push(P(d[e.to],e.to));
}
if(d1[e.to]>d2 && d[e.to]<d2){
d1[e.to]=d2;
que.push(P(d1[e.to],e.to));
}
}
}
printf("%lld\n",d1[n]);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) G[i].clear();
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
G[u].push_back(node(v,w));
G[v].push_back(node(u,w));
}
solve();
}
return 0;
}
最大流
DINIC
struct Edge{
int from, to, cap, flow;
};
struct Dinic{
int n, m, s, t;
vector<Edge> edges;
vector<int> G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void ClearAll(int n){
for(int i = 0; i < n; i++) G[i].clear();
edges.clear();
}
void ClearFlow(){
for(int i=0;i<edges.size();i++) edges[i].flow=0;
}
void AddEdge(int from, int to, int cap){
edges.push_back((Edge){from, to, cap, 0});
edges.push_back((Edge){to, from, 0, 0});
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS(){
memset(vis, 0, sizeof(vis));
queue<int> Q;
Q.push(s);
vis[s] = 1;
d[s] = 0;
while(!Q.empty()) {
int x = Q.front(); Q.pop();
for(int i = 0; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(!vis[e.to] && e.cap > e.flow) {
vis[e.to] = 1;
d[e.to] = d[x] + 1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x, int a){
if(x == t || a == 0) return a;
int flow = 0, f;
for(int& i = cur[x]; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) {
e.flow += f;
edges[G[x][i]^1].flow -= f;
flow += f;
a -= f;
if(a == 0) break;
}
}
return flow;
}
int Maxflow(int s, int t){
this->s = s; this->t = t;
int flow = 0;
while(BFS()) {
memset(cur, 0, sizeof(cur));
flow += DFS(s, inf);
}
return flow;
}
vector<int>Mincut(){
vector<int> ans;
for(int i=0;i<edges.size();i++){
Edge& e=edges[i];
if(vis[e.from] && !vis[e.to] && e.cap>0) ans.push_back(i);
}
return ans;
}
void Reduce(){
for(int i=0;i<edges.size();i++) edges[i].cap-=edges[i].flow;
}
}g;
ISAP
#include<bits/stdc++.h>
#define maxn 550
#define inf 0x3f3f3f3f
using namespace std;
struct Edge{
int from, to, cap, flow;
};
struct ISAP{
int n, m, s, t;
vector<Edge> edges;
vector<int> G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
int p[maxn];
int num[maxn];
void AddEdge(int from, int to, int cap){
edges.push_back((Edge){from, to, cap, 0});
edges.push_back((Edge){to, from, 0, 0});
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS(){
memset(vis, 0, sizeof(vis));
queue<int> Q;
Q.push(t);
vis[t] = 1;
d[t] = 0;
while(!Q.empty()){
int x = Q.front(); Q.pop();
for(int i = 0; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]^1];
if(!vis[e.from] && e.cap > e.flow) {
vis[e.from] = 1;
d[e.from] = d[x] + 1;
Q.push(e.from);
}
}
}
return vis[s];
}
void ClearAll(int n){
this->n = n;
for(int i = 0; i < n; i++) G[i].clear();
edges.clear();
}
int Augment(){
int x = t, a = inf;
while(x != s) {
Edge& e = edges[p[x]];
a = min(a, e.cap-e.flow);
x = edges[p[x]].from;
}
x = t;
while(x != s) {
edges[p[x]].flow += a;
edges[p[x]^1].flow -= a;
x = edges[p[x]].from;
}
return a;
}
int Maxflow(int s, int t){
this->s = s; this->t = t;
int flow = 0;
BFS();
memset(num, 0, sizeof(num));
for(int i = 0; i < n; i++) num[d[i]]++;
int x = s;
memset(cur, 0, sizeof(cur));
while(d[s] < n) {
if(x == t) {
flow += Augment();
x = s;
}
int ok = 0;
for(int i = cur[x]; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(e.cap > e.flow && d[x] == d[e.to] + 1) {
ok = 1;
p[e.to] = G[x][i];
cur[x] = i;
x = e.to;
break;
}
}
if(!ok) {
int m = n-1;
for(int i = 0; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(e.cap > e.flow) m = min(m, d[e.to]);
}
if(--num[d[x]] == 0) break;
num[d[x] = m+1]++;
cur[x] = 0;
if(x != s) x = edges[p[x]].from;
}
}
return flow;
}
};
ISAP g;
int main(){
// freopen("input.txt","r",stdin);
ios::sync_with_stdio(false);
cin.tie(0);
int t,flag=1; cin>>t;
while(t--){
int x,y;
cin>>x>>y;
g.ClearAll(x);
for(int i=1;i<=y;i++){
int u,v,w;
cin>>u>>v>>w;
g.AddEdge(u,v,w);
}
cout<<"Case "<<flag++<<": "<<g.Maxflow(1,x)<<endl;
}
return 0;
}
MCMF(最小费用最大流)
struct Edge{
int from,to,cap,flow,cost;
Edge(int from,int to,int cap,int flow,int cost):from(from),to(to),cap(cap),flow(flow),cost(cost){}
};
struct MCMF{
int n,m,s,t;
vector<Edge>edges;
vector<int>G[maxn];
int inq[maxn],d[maxn],p[maxn],a[maxn];
void init(int n){
this->n=n;
for(int i=0;i<n;i++) G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap,int cost){
edges.push_back(Edge(from,to,cap,0,cost));
edges.push_back(Edge(to,from,0,0,-cost));
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BellmanFord(int s,int t,int &flow,int& cost){
for(int i=0;i<n;i++) d[i]=inf;
memset(inq,0,sizeof(inq));
d[s]=0,inq[s]=1,p[s]=0,a[s]=inf;
queue<int>Q;
Q.push(s);
while(!Q.empty()){
int u=Q.front(); Q.pop();
inq[u]=0;
for(int i=0;i<G[u].size();i++){
Edge& e=edges[G[u][i]];
if(e.cap>e.flow && d[e.to]>d[u]+e.cost){
d[e.to]=d[u]+e.cost;
p[e.to]=G[u][i];
a[e.to]=min(a[u],e.cap-e.flow);
if(!inq[e.to]){
Q.push(e.to);
inq[e.to]=1;
}
}
}
}
if(d[t]==inf) return false;
flow+=a[t];
cost+=d[t]*a[t];
int u=t;
while(u!=s){
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
u=edges[p[u]].from;
}
return true;
}
int Mincost(int s,int t){
int flow=0,cost=0;
while(BellmanFord(s,t,flow,cost));
return cost;
}
};
最大流最小割
题目要求
求出最小割的容量(等价最大流)
同时求出图G最小割S集合的点数以及集合内点的编号
若有多解输出一组即可
#include<bits/stdc++.h>
#define maxn 550
#define inf 0x3f3f3f3f
using namespace std;
int flag,num;
struct Edge{
int from, to, cap, flow;
};
struct Dinic{
int n, m, s, t;
vector<Edge> edges;
vector<int> G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void ClearAll(int n){
for(int i = 0; i < n; i++) G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap){
edges.push_back((Edge){from, to, cap, 0});
edges.push_back((Edge){to, from, 0, 0});
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS(){
memset(vis, 0, sizeof(vis));
queue<int> Q;
Q.push(s);
vis[s] = 1;
d[s] = 0;
if(flag) num++;
while(!Q.empty()) {
int x = Q.front(); Q.pop();
for(int i = 0; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(!vis[e.to] && e.cap > e.flow) {
vis[e.to] = 1;
d[e.to] = d[x] + 1;
if(flag==0 && e.to==n) return 1;
if(flag) num++;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x, int a){
if(x == t || a == 0) return a;
int flow = 0, f;
for(int& i = cur[x]; i < G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) {
e.flow += f;
edges[G[x][i]^1].flow -= f;
flow += f;
a -= f;
if(a == 0) break;
}
}
return flow;
}
int Maxflow(int s, int t){
this->s = s; this->t = t;
int flow = 0;
while(BFS()){
memset(cur, 0, sizeof(cur));
flow += DFS(s, inf);
}
flag=1;
flag=BFS();
return flow;
}
};
Dinic g;
int main(){
freopen("input.txt","r",stdin);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
flag=0,num=0;
int x,y;
cin>>x>>y;
g.ClearAll(x);
for(int i=1;i<=y;i++){
int u,v,w;
cin>>u>>v>>w;
g.AddEdge(u,v,w);
}
cout<<g.Maxflow(1,x)<<" ";
cout<<num<<endl;
for(int i=1;i<=x;i++){
if(g.vis[i]){
if(!flag) flag=1;
else cout<<" ";
cout<<i;
}
}
cout<<endl;
return 0;
}
最大团
void BronKerbosch(int id,int lenAll,int lenSome,int lenNone){
if(lenSome==0 && lenNone==0){
if(lenAll>mx) mx=lenAll;
return ;
}
if(lenSome==0) return ;
int i,j,u,v,tid,tlenAll,tlenSome,tlenNone;
u=Some[id][1]; tid=id+1;
for(i=1;i<=lenSome;i++){
v=Some[id][i];
if(g[u][v]) continue;
tlenAll=lenAll+1;
for(j=1;j<=lenAll;j++) All[tid][j]=All[id][j];
All[tid][tlenAll]=v;
tlenSome=0;
for(j=1;j<=lenSome;j++)
if(g[v][Some[id][j]])
Some[tid][++tlenSome]=Some[id][j];
tlenNone=0;
for(j=1;j<=lenNone;j++)
if(g[v][None[id][j]])
None[tid][++tlenNone]=None[id][j];
BronKerbosch(tid,tlenAll,tlenSome,tlenNone);
}
}
void solve(){
int i,j;
for(i=1;i<=n;i++) Some[0][i]=i;
BronKerbosch(0,0,n,0);
}
Tarjan
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
struct tarjan{ //复杂度O(N+M)
const static int MAXN = 1e4 + 7; // 点数
const static int MAXM = 1e5 + 7; // 边数
struct Edge{
int to, nxt;
Edge(){}
Edge(int t, int n):to(t), nxt(n){}
} edge[MAXM];
int head[MAXN], tot, n;
int Low[MAXN], DFN[MAXN], Stack[MAXN], Belong[MAXN];//Belong数组的值是1~scc
int Index, top;
int scc; // 强连通分量的个数
int num[MAXN]; // 各个强连通分量包含点的个数,数组编号1~scc //num数组不一定需要,结合实际情况
bool Instack[MAXN];
inline void init(int n){
tot = 0; this->n = n;
memset(head, -1, sizeof(head));
}
inline void addedge(int u, int v){
edge[tot] = Edge(v, head[u]);
head[u] = tot++;
}
void Tarjan(int u){
int v;
Low[u] = DFN[u] = ++Index;
Stack[top++] = u;
Instack[u] = true;
for(int i = head[u]; ~i; i = edge[i].nxt){
v = edge[i].to;
if (!DFN[v]){
Tarjan(v);
Low[u] = min(Low[u], Low[v]);
} else if(Instack[v] && Low[u] > DFN[v])
Low[u] = DFN[v];
}
if (Low[u] == DFN[u]){
scc++;
do{
v = Stack[--top];
Instack[v] = false;
Belong[v] = scc;
num[scc]++;
}while(v != u);
}
}
int indeg[MAXN], outdeg[MAXN];
inline int solve(int n){
memset(DFN, 0, sizeof(DFN));
memset(num, 0, sizeof(num));
memset(Instack, 0, sizeof(Instack));
Index = scc = top = 0;
for(int i = 1; i <= n; i++) if(!DFN[i]) Tarjan(i);
// for this problem 缩点
memset(indeg, 0, sizeof(indeg));
memset(outdeg, 0, sizeof(outdeg));
if (scc == 1) return 0;
for(int u = 1; u <= n; u++)
for(int i = head[u]; i!=-1; i = edge[i].nxt){
int v = edge[i].to;
if (Belong[u] != Belong[v]){
outdeg[Belong[u]]++;
indeg[Belong[v]]++;
}
}
int ans1 = 0, ans2 = 0;
for(int i = 1; i <= scc; i++){
if (indeg[i] == 0) ans1++;
if (outdeg[i] == 0) ans2++;
}
// 至少加几条边让整个图变成强连通(即,出度或入度的最大值)
return max(ans1, ans2);
}
} g;
求三元环
sum为某一边所含三元环。
#include<bits/stdc++.h>
#define maxn 100050
#define LL long long
#define mm(a,b) memset(a,b,sizeof(a))
using namespace std;
set<LL>s;
int n,m,limit;
vector<int>e[maxn];
int out[maxn],vis[maxn],linker[maxn];
void init(){
s.clear();
for(int i=1;i<=n;i++) e[i].clear();
mm(out,0),mm(vis,0),mm(linker,0);
limit=sqrt(m+0.5);
}
int main(){
// freopen("input.txt","r",stdin);
while(scanf("%d%d",&n,&m)!=EOF){
init();
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
out[v]++,out[u]++;
s.insert((LL)u*n+v);
s.insert((LL)v*n+u);
}
LL ans=0;
for(int i=1;i<=n;i++){
vis[i]=1;
int len=e[i].size();
for(int j=0;j<len;j++){
int next=e[i][j];
linker[next]=i;
}
for(int j=0;j<len;j++){
int next=e[i][j];
int sum=0;
if(vis[next]) continue;
if(out[next]<=limit){
int len2=e[next].size();
for(int k=0;k<len2;k++){
int next2=e[next][k];
if(linker[next2]==i) sum++;
}
}
else{
for(int k=0;k<len;k++){
int next2=e[i][k];
if(s.find((LL)next2*n+next)!=s.end()) sum++;
}
}
ans+=(LL)sum*(sum-1)/2;
}
}
printf("%lld\n",ans);
}
return 0;
}
树
LCA
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define MAXN 100010
struct node{
int y,next,w;
}e[MAXN*2];
int n,m,len,head[MAXN],deep[MAXN],f[MAXN],anc[MAXN][25];
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
void insert(int x,int y,int w){
e[++len].next=head[x];
e[len].w=w;
head[x]=len;e[len].y=y;
}
void dfs(int x,int fa){
anc[x][0]=f[x];
for(int i=1;i<=20;i++) anc[x][i]=anc[anc[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].next)
if(e[i].y!=fa){
f[e[i].y]=x;
deep[e[i].y]=deep[x]+1;
dfs(e[i].y,x);
}
}
int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(deep[anc[x][i]]>=deep[y]) x=anc[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--)
if(anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
return f[x];
}
int main(){
freopen("input.txt","r",stdin);
while(cin>>n){
len=0;
memset(head,0,sizeof head);
for(int i=1;i<n;i++){
int x=read(),y=read(),w=read();
insert(x,y,w);
insert(y,x,w);
}
memset(f,0,sizeof(f));
memset(anc,0,sizeof(anc));
memset(deep,0,sizeof(deep));
deep[1]=1;
dfs(1,0);
//father = lca(x,y);
}
return 0;
}
树上距离
预处理任意一点的dist(id出发到各点的距离),
树上x到y的距离为dist[x]+dist[y]-2dist[lca(x,y)]。
void dfs(int id){
for(int i=0;i<e[id].size();i++){
int next=e[id][i].to,ww=e[id][i].w;
if(vis[next]) continue;
dis[next]=dis[id]+ww;
vis[next]=1;
dfs2(next);
}
}
DFS序
void dfs(int x,int fa){
in[x]=step;
int len=e[x].size();
for(int i=0;i<len;i++){
int next=e[x][i];
if(next==fa) continue;
step++;
dfs(next,x);
}
out[x]=step;
}
树上路径交
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500005;
struct node{
int to,w;
node(int to,int w):to(to),w(w){}
};
vector<node> e[maxn];
int dist[maxn],n,m,Q;
struct path{
int a,b,c;
path(){}
path(int A,int B,int C) {a=A,b=B,c=C;}
}p[maxn],T[maxn<<2];
int deep[maxn],f[maxn],anc[maxn][25];
void dfs(int x,int fa){
anc[x][0]=f[x];
for(int i=1;i<=20;i++) anc[x][i]=anc[anc[x][i-1]][i-1];
for(int i=0;i<e[x].size();i++){
int next=e[x][i].to;
if(next!=fa){
f[next]=x;
deep[next]=deep[x]+1;
dist[next]=dist[x]+e[x][i].w;
dfs(next,x);
}
}
}
int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
for(int i=20;i>=0;i--) if(deep[anc[x][i]]>=deep[y]) x=anc[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--) if(anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
return f[x];
}
bool cmp(int a,int b){
return deep[a]<deep[b];
}
path mix(path x,path y){
if(!x.c||!y.c) return path(0,0,0);
int cs[6];
cs[1]=lca(x.a,y.a);
cs[2]=lca(x.a,y.b);
cs[3]=lca(x.b,y.a);
cs[4]=lca(x.b,y.b);
sort(cs+1,cs+1+4,cmp);
int md=max(deep[x.c],deep[y.c]),nd=min(deep[x.c],deep[y.c]);
if(deep[cs[1]]<nd||deep[cs[3]]<md) return path(0,0,0);
else return path(cs[3],cs[4],lca(cs[3],cs[4]));
}
void init(){
memset(f,0,sizeof(f));
memset(anc,0,sizeof(anc));
memset(deep,0,sizeof(deep));
memset(dist,0,sizeof(dist));
deep[1]=1;
dfs(1,0);
}
void build(int l,int r,int tp){
if(l==r){
T[tp]=p[l];
return;
}
int mid=l+r>>1;
build(l,mid,tp<<1);
build(mid+1,r,tp<<1|1);
T[tp]=mix(T[tp<<1],T[tp<<1|1]);
}
path query(int l,int r,int tp,int L,int R){
if(L<=l&&r<=R) return T[tp];
int mid=l+r>>1;
if(R<=mid) return query(l,mid,tp<<1,L,R);
if(L>mid) return query(mid+1,r,tp<<1|1,L,R);
return mix(query(l,mid,tp<<1,L,R),query(mid+1,r,tp<<1|1,L,R));
}
int main(){
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++) e[i].clear();
for(int i=1;i<n;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
e[u].push_back(node(v,w));
e[v].push_back(node(u,w));
}
init();
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&p[i].a,&p[i].b);
p[i].c = lca(p[i].a,p[i].b);
}
build(1,m,1);
scanf("%d",&Q);
for(int i=1;i<=Q;i++){
int a,b;
scanf("%d%d",&a,&b);
path ans = query(1,m,1,a,b);
printf("%lld\n",dist[ans.a]+dist[ans.b]-2*dist[ans.c]);
}
}
return 0;
}
树链剖分
#include <bits/stdc++.h>
#define N 30005
#define M 60005
using namespace std;
int n,q,cnt,sz;//sz 线段树_number
int v[N],dep[N],size[N],head[N],fa[N];
int pos[N],bl[N];
//pos[i] 线段树_number
//bl[i] top[i]
struct node{
int to,nxt;
}e[M];
void insert(int u,int v){
e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
}
struct Tnd{
int l,r,mx,sum;
}T[N<<2];
void dfs1(int id){
size[id]=1;
for(int i=head[id];i;i=e[i].nxt){
if(e[i].to == fa[id]) continue;
dep[e[i].to] = dep[id] + 1;
fa[e[i].to] = id;
dfs1(e[i].to);
size[id] += size[e[i].to];
}
}
void dfs2(int id,int chain){
int k=0;sz++;
pos[id] = sz;
bl[id] = chain;
for(int i=head[id];i;i=e[i].nxt)
if(dep[e[i].to] > dep[id] && size[e[i].to] > size[k])
k=e[i].to;
if(k==0)
return;
dfs2(k,chain);
for(int i=head[id];i;i=e[i].nxt)
if(dep[e[i].to] > dep[id] && e[i].to!=k)
dfs2(e[i].to,e[i].to);
}
void build(int tp,int l,int r){
T[tp].l=l;T[tp].r=r;
if(l==r) return;
int mid = (l+r)>>1;
build(tp<<1,l,mid);
build(tp<<1|1,mid+1,r);
}
void change(int tp,int p,int delta){
int l=T[tp].l,r=T[tp].r,mid=(l+r)>>1;
if(l==r){
T[tp].sum = T[tp].mx = delta;
return;
}
if(p<=mid)
change(tp<<1,p,delta);
else
change(tp<<1|1,p,delta);
T[tp].sum = T[tp<<1].sum + T[tp<<1|1].sum;
T[tp].mx = max(T[tp<<1].mx,T[tp<<1|1].mx);
}
int querymx(int tp,int x,int y){
int l = T[tp].l,r = T[tp].r,mid=(l+r)>>1;
if(l==x && r==y)
return T[tp].mx;
else if(y<=mid) return querymx(tp<<1,x,y);
else if(x>mid) return querymx(tp<<1|1,x,y);
else return max(querymx(tp<<1,x,mid),querymx(tp<<1|1,mid+1,y));
}
int querysum(int tp,int x,int y){
int l = T[tp].l,r = T[tp].r,mid=(l+r)>>1;
if(l==x && r==y)
return T[tp].sum;
else if(y<=mid) return querysum(tp<<1,x,y);
else if(x>mid) return querysum(tp<<1|1,x,y);
else return querysum(tp<<1,x,mid)+querysum(tp<<1|1,mid+1,y);
}
int solvemx(int x,int y){
int mx=-INT_MAX;
while(bl[x]!=bl[y]){
if(dep[bl[x]]<dep[bl[y]])
swap(x,y);
mx=max(mx,querymx(1,pos[bl[x]],pos[x]));
x=fa[bl[x]];
}
if(pos[x]>pos[y])
swap(x,y);
mx=max(mx,querymx(1,pos[x],pos[y]));
return mx;
}
int solvesum(int x,int y){
int sum=0;
while(bl[x]!=bl[y]){
if(dep[bl[x]]<dep[bl[y]])
swap(x,y);
sum+=querysum(1,pos[bl[x]],pos[x]);
x=fa[bl[x]];
}
if(pos[x]>pos[y])
swap(x,y);
sum+=querysum(1,pos[x],pos[y]);
return sum;
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
insert(x,y);insert(y,x);
}
for(int i=1;i<=n;i++)
scanf("%d",&v[i]);
dfs1(1);
dfs2(1,1);
build(1,1,n);
for(int i=1;i<=n;i++)
change(1,pos[i],v[i]);
scanf("%d",&q);
char cho[20];
int x,y;
for(int i=1;i<=q;i++){
scanf("%s%d%d",cho,&x,&y);
if(cho[0]=='C'){
v[x]=y;
change(1,pos[x],y);
} else {
if(cho[1]=='M'){
printf("%d\n",solvemx(x,y));
} else {
printf("%d\n",solvesum(x,y));
}
}
}
return 0;
}
点分治
求树上dist(u,v)<=k点对.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
using namespace std;
typedef long long ll;
const int maxn = 10000+5;
const int maxm = 20000+10;
bool vis[maxn];
int head[maxn];
int nxt[maxm],w[maxm],e[maxm],n,k,root,sum,x,y,z;
int ans;
int F[maxn];//sum of son
int son[maxn];
int dep[maxn];
int d[maxn];
void getroot(int x,int fa){
son[x]=1;F[x]=0;
for(int k=head[x];k!=-1;k=nxt[k]){
if(e[k]==fa||vis[e[k]]) continue;
getroot(e[k],x);
son[x]+=son[e[k]];
F[x]=max(F[x],son[x]);
}
F[x]=max(F[x],sum-F[x]);
if(F[x]<F[root]) root=x;
}
void getdep(int x,int fa){
dep[++dep[0]]=d[x];
for(int k=head[x];k!=-1;k=nxt[k]){
if(e[k]==fa||vis[e[k]]) continue;
d[e[k]]=d[x]+w[k];
getdep(e[k],x);
}
}
int calc(int x,int v){
d[x]=v;dep[0]=0;
getdep(x,0);
sort(dep+1,dep+1+dep[0]);
int l=1,r=dep[0],ret=0;
while(l<r){
if(dep[r]+dep[l]<=k){
ret+=r-l;
l++;
} else {
r--;
}
}
return ret;
}
void solve(int x){
ans+=calc(x,0);
vis[x]=1;
for(int k=head[x];k!=-1;k=nxt[k]){
if(vis[e[k]]) continue;
ans-=calc(e[k],w[k]);
sum=son[e[k]];
root=0;
getroot(e[k],0);
solve(root);
}
}
int main(){
//freopen("in.txt","r",stdin);
while(~scanf("%d%d",&n,&k)){
if(!n&&!k) break;
ans=root=0;
memset(vis,0,sizeof vis);
memset(head,-1,sizeof head);
for(int i=1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
e[i]=y;w[i]=z;nxt[i]=head[x];head[x]=i;
e[i+n]=x;w[i+n]=z;nxt[i+n]=head[y];head[y]=i+n;
}
F[0]=INT_MAX;sum=n;
getroot(1,0);
solve(root);
printf("%d\n",ans);
}
return 0;
}
数据结构
树状数组
void change(int x,int val){
while(x<=n) {
tree[x]+=val;
x+=x&(-x);
}
}
int sum(int x){
int res=0;
while (x){
res+=tree[x];
x-=x&(-x);
}
return res;
}
二维树状数组
int sum(int i,int j){
int s=0;
while(i>0){
int jj=j;
while(jj>0){
s+=tree[i][jj];
jj-=jj&-jj;
}
i-=i&-i;
}
return s;
}
void add(int i,int j,int x){
while(i<=n){
int jj=j;
while(jj<=n) {
tree[i][jj]+=x;
jj+=jj&-jj;
}
i+=i&-i;
}
}
李超线段树
#include <cstdio>
#include <cmath>
#include <algorithm>
#define N 39989
using namespace std;
template<typename T> inline void read(T &x) {
x = 0; T f = 1; char ch; do {ch = getchar(); if (ch == '-')f = -1;} while (ch < '0' || ch > '9'); do x = x * 10 + ch - '0', ch = getchar(); while (ch <= '9' && ch >= '0'); x *= f;
}
template<typename A, typename B> inline void read(A&x, B&y) {read(x); read(y);}
template<typename A, typename B, typename C> inline void read(A&x, B&y, C&z) {read(x); read(y); read(z);}
template<typename A, typename B, typename C, typename D> inline void read(A&x, B&y, C&z, D&w) {read(x); read(y); read(z); read(w);}
struct Seg {
double k, b;
Seg() {}
Seg(int x0, int y0, int x1, int y1) {
if (x0 == x1)k = 0, b = max(y0, y1);
else k = 1.0 * (y0 - y1) / (x0 - x1), b = -k * x0 + y0;
}
double gety(int x) {return k * x + b;}
} s[100010];
int m, op, cnt, X0, Y0, X1, Y1, ans, v[131000];
inline int sig(double x) {return fabs(x) < 1e-8 ? 0 : (x > 0 ? 1 : -1);}
void ins(int x, int a, int b, int c, int d, int p) {
if (c <= a && b <= d) {
if (sig(s[p].gety(a) - s[v[x]].gety(a)) > 0
&& sig(s[p].gety(b) - s[v[x]].gety(b)) > 0) {v[x] = p; return;}
if (sig(s[p].gety(a) - s[v[x]].gety(a)) <= 0
&& sig(s[p].gety(b) - s[v[x]].gety(b)) <= 0)return;
if (a == b)return;
}
int mid = (a + b) >> 1;
if (c <= mid)ins(x << 1, a, mid, c, d, p);
if (d > mid)ins(x << 1 | 1, mid + 1, b, c, d, p);
}
void ask(int x, int a, int b, int c) {
if (sig(s[ans].gety(c) - s[v[x]].gety(c)) < 0)ans = v[x];
else if (!sig(s[ans].gety(c) - s[v[x]].gety(c)) && ans > v[x])ans = v[x];
if (a == b)return;
int mid = (a + b) >> 1;
c <= mid ? ask(x << 1, a, mid, c) : ask(x << 1 | 1, mid + 1, b, c);
}
int main() {
freopen("in.txt", "r", stdin);
s[0].b = -1;
read(m);
int lastans = 0;
while (m--) {
read(op);
if (!op) {
read(X0);
X0 = (X0 + lastans - 1) % N + 1;
ans = 0, ask(1, 1, N, X0);
printf("%d\n", ans);
lastans = ans;
} else {
read(X0), read(Y0), read(X1), read(Y1);
X0 = (X0 + lastans - 1) % N + 1; Y0 = (Y0 + lastans - 1) % 1000000000 + 1;
X1 = (X1 + lastans - 1) % N + 1; Y1 = (Y1 + lastans - 1) % 1000000000 + 1;
if (X0 > X1) swap(X0, X1), swap(Y0, Y1);
s[++cnt] = Seg(X0, Y0, X1, Y1);
ins(1, 1, N, X0, X1, cnt);
}
}
}
主席树
区间k小。
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 7;
int arr[N]; //arr[] 原数组的数在rank[] 中的位置;
int Rank[N]; //rank[] 原数组离散化
struct ChairTree{
#define lson tree[rt].lc, tree[rt1].lc, l, m
#define rson tree[rt].rc, tree[rt1].rc, m+1, r
struct node{
int lc, rc, w;
node(){}
} tree[N * 20];
int root[N], cnt;
void build(){
root[0] = cnt = 0;
memset(tree, 0, sizeof(tree));
}
void add(int pos, int val, int &rt, int rt1, int l, int r){
tree[rt = ++cnt] = tree[rt1];
tree[rt].w += val;
if (l == r) return;
int m = (l + r) >> 1;
if (pos <= m) add(pos, val, lson);
else add(pos, val, rson);
}
int query(int k, int rt, int rt1, int l, int r){
if (l == r) return l;
int lsize = tree[tree[rt1].lc].w - tree[tree[rt].lc].w;
int m = (l + r) >> 1;
if (lsize >= k) return query(k, lson);
else return query(k - lsize, rson);
}
} T;
int main(){
//freopen("in.txt","r",stdin);
int _, l, r, k, n, q;
for (; ~scanf("%d%d", &n, &q);){
T.build();
for (int i = 1; i <= n; i++) {
scanf("%d", &arr[i]);
Rank[i] = arr[i];
}
sort(Rank + 1, Rank + n+1);//Rank 存储原值
int m = unique(Rank + 1, Rank + n +1) - (Rank + 1);
for (int i = 1; i <= n; i++) {//离散化后的数组,仅仅用来更新
arr[i] = lower_bound(Rank + 1, Rank + n+1, arr[i]) - Rank;
}
for (int i = 1; i <= n; i++){
T.add(arr[i], 1, T.root[i], T.root[i-1], 1, n);
}
for (; q--;){
scanf("%d%d%d", &l, &r, &k);
int pos = T.query(k, T.root[l-1], T.root[r], 1, n);
printf("%d\n", Rank[pos]);
}
}
return 0;
}
可持久化01Trie
Query,[l,r]异或x最大的值。
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int bin[30];
int n,m;
int a[N],root[N];
struct trie{
int cnt;
int ch[N*30][2],sum[N*30];
void init(){
cnt = 0;
memset(ch,0,sizeof ch);
memset(sum,0,sizeof sum);
}
int insert(int x,int val){
int tmp,y;tmp=y=++cnt;
for(int i=29;i>=0;i--)
{
ch[y][0]=ch[x][0];ch[y][1]=ch[x][1];
sum[y]=sum[x]+1;
int t=val&bin[i];t>>=i;
x=ch[x][t];
ch[y][t]=++cnt;
y=ch[y][t];
}
sum[y]=sum[x]+1;
return tmp;
}
int query(int l,int r,int val){
int tmp=0;
for(int i=29;i>=0;i--)
{
int t=val&bin[i];t>>=i;
if(sum[ch[r][t^1]]-sum[ch[l][t^1]])
tmp+=bin[i],r=ch[r][t^1],l=ch[l][t^1];
else r=ch[r][t],l=ch[l][t];
}
return tmp;
}
}trie;
int in[N],out[N],cnt;
vector<int> e[N];
void dfs(int id,int fa){
in[id] = ++cnt;
root[cnt] = trie.insert(root[cnt-1],a[id]);
for(auto ep:e[id]){
if(ep == fa) continue;
dfs(ep,id);
}
out[id]=cnt;
}
int main(){
bin[0]=1;for(int i=1;i<=30;i++)bin[i]=bin[i-1]<<1;
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++) e[i].clear();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=2;i<=n;i++){
int v;
v=read();
e[v].push_back(i);
}
cnt=0;
trie.init();
dfs(1,0);
int u,x;
while(m--){
u=read();x=read();
printf("%d\n",trie.query(root[in[u]-1],root[out[u]],x));
}
}
return 0;
}
平衡树
Treap
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cassert>
using namespace std;
struct Node{
Node *ch[2];
int r, v, s;//s 表示节点数
Node(int v):v(v){
ch[0]=ch[1]=NULL;
r = rand();//在cstdlib 头声明
s = 1;
}
int cmp(int x){
if (x == v) return -1;
return x<v ? 0 : 1;
}
void maintain(){
s = 1;
if(ch[0]!=NULL) s+=ch[0]->s;
if(ch[1]!=NULL) s+=ch[1]->s;
}
}; //root 全局使用的话可以在这里跟上*root
void rotate(Node* &o,int d){
Node *k=o->ch[d^1];
o->ch[d^1]=k->ch[d];
k->ch[d]=o;
o->maintain();
k->maintain();
o=k;
}
void insert(Node* &o,int x){//o 子树中事先不存在x
if(o==NULL) o=new Node(x);
else{
//如这里改成int d=o->cmp(x);
//就不可以插入相同的值,因为d 可能为-1
int d=x<(o->v)?0:1;
insert(o->ch[d],x);
if(o->ch[d]->r > o->r)
rotate(o,d^1);
}
o->maintain();
}
void remove(Node* &o,int x){
if (o==NULL) return ;//空时返回
int d=o->cmp(x);
if (d == -1){
Node *u=o;
if(o->ch[0] && o->ch[1]){
int d2=(o->ch[0]->r < o->ch[1]->r)?0:1;
rotate(o,d2);
remove(o->ch[d2],x);
}else{
if(o->ch[0]==NULL) o=o->ch[1];
else o=o->ch[0];
delete u;//这个要放里面
}
}
else remove(o->ch[d],x);
if(o) o->maintain();//之前o 存在,但是删除节点后o 可能就是空NULL 了,所以需要先判断o 是否为空
}
//返回关键字从小到大排序时的第k 个值
//若返回第K 大的值,只需要把ch[0] 和ch[1] 全互换就可以了
int kth(Node* o,int k){
assert(o && k>=1 && k<=o->s);//保证输入合法,根据实际问题返回
int s=(o->ch[0]==NULL)?0:o->ch[0]->s;
if(k==s+1) return o->v;
else if(k<=s) return kth(o->ch[0],k);
else return kth(o->ch[1],k-s-1);
}
//返回值x在树中的排名,就算x 不在o 树中也能返回排名
//返回值范围在[1,o->s+1] 范围内
int RANK(Node* o,int x){
if(o==NULL) return 1;//未找到x;
int num= o->ch[0]==NULL ? 0:o->ch[0]->s;
if(x==o->v) return num+1;
else if(x < o->v) return RANK(o->ch[0],x);
else return RANK(o->ch[1],x)+num+1;
}
int main(){
int n=0, v;
while(scanf("%d",&n)==1 && n){
Node *root=NULL; //初始化为NULL
for(int i=0; i<n; i++){
int x;
scanf("%d",&x);
if(root==NULL) root=new Node(x);
else insert(root,x);
}
while(scanf("%d",&v)==1){
printf("%d\n",RANK(root,v));
}
}
return 0;
}
AVL树
#include<cstdio>
#include<iostream>
#include<algorithm>
#define INF 0xfffffff
#define BASE 1000000
using namespace std;
int ans=0;
struct Node{
int x,bf,h;//bf=balance factor,h=height
Node *l,*r;
};
class AVLTree{
public:
void Init() { rt = NULL; }
int H(Node *T){return (T==NULL)?0:T->h;}
int BF(Node *l,Node *r){//get balance factor
if (NULL==l && NULL==r) return 0;
else if (NULL == l) return -r->h;
else if (NULL == r) return l->h;
return l->h - r->h;
}
Node *Lrorate(Node *a){//left rorate
Node *b;
b=a->r;
a->r=b->l;
b->l=a;
a->h=max(H(a->l),H(a->r)) + 1;
b->h=max(H(b->l),H(b->r)) + 1;
a->bf=BF(a->l,a->r);
b->bf=BF(b->l,b->r);
return b;
}
Node *Rrorate(Node *a){//right rorate
Node *b;
b=a->l;
a->l=b->r;
b->r=a;
a->h=max(H(a->l),H(a->r)) + 1;
b->h=max(H(b->l),H(b->r)) + 1;
a->bf=BF(a->l,a->r);
b->bf=BF(b->l,b->r);
return b;
}
Node *LRrorate(Node *a){//left then right
a->l = Lrorate(a->l);
Node *c;
c=Rrorate(a);
return c;
}
Node *RLrorate(Node *a){//right then left
a->r=Rrorate(a->r);
Node *c;
c=Lrorate(a);
return c;
}
void Insert(int x){_Insert(rt,x);}
void _Insert (Node *&T,int x){
if (NULL==T){
T=(Node*)malloc(sizeof(Node));
T->x=x;
T->bf=0;T->h=1;
T->l=T->r=NULL;
return ;
}
if (x < T->x) _Insert(T->l,x);
else if (x > T->x) _Insert(T->r,x);
else return ; //error :the same y
T->h=max(H(T->l),H(T->r))+1;//maintain
T->bf=BF(T->l,T->r);
if (T->bf > 1 || T->bf < -1){//not balanced
if (T->bf > 0 && T->l->bf > 0)T=Rrorate(T);
else if (T->bf < 0 && T->r->bf < 0)T=Lrorate(T);
else if (T->bf > 0 && T->l->bf < 0)T=LRrorate(T);
else if (T->bf < 0 && T->r->bf > 0)T=RLrorate(T);
}
}
void GetPet(int x){//get pet or person
if (NULL==rt){return ;}
int small=0,large=INF;
//printf("x=%d\n",x);
int flag;
if (Find(rt,x,small,large)){
printf("find %d\n",x);
_Delete(rt,x);
}else if (small==0)flag=1;
else if (large==INF)flag=0;
else if (large-x<x-small)flag=1;
else flag=0;
if (!flag){//choose large
_Delete(rt,small);
ans=(ans+x-small)%BASE;
}else {
_Delete(rt,large);
ans=(ans+large-x)%BASE;
}
}
bool Find(Node *T,int x,int &small,int &large){
if (NULL==T)return 0;
if (x==T->x)return 1;
if (x<T->x){
large=min(large,T->x);
return Find(T->l,x,small,large);
}else{
small=max(small,T->x);
return Find(T->r,x,small,large);
}
}
void _Delete(Node *&T,int x){
if (NULL==T)return ;
if (x < T->x){//y at left
_Delete(T->l,x);
T->bf=BF(T->l,T->r);
if (T->bf<-1){
if (1==T->r->bf)T=RLrorate(T);
else T=Lrorate(T);//bf==0 or -1
}
}else if (x > T->x){//y at right
_Delete(T->r,x);
T->bf=BF(T->l,T->r);
if (T->bf>1){
if (-1==T->l->bf)T=LRrorate(T);
else T=Rrorate(T);//bf==0 or 1
}
}else {//here is x
if (T->l&&T->r){//left &&right
Node *t=T->l;
while (t->r)t=t->r;
T->x=t->x;
_Delete(T->l,t->x);
T->bf=BF(T->l,T->r);
if (T->bf<-1){
if (1==T->r->bf)T=RLrorate(T);
else T=Lrorate(T);//bf==0 or -1
}
}else {//left || right
Node *t=T;
if (T->l)T=T->l;
else if(T->r)T=T->r;
else {free(T);T=NULL;}
if (T)free(t);
}
}
}
//Debug,you will not need it at this problem
void show(){InOrder(rt);puts("EndShow");}
void InOrder(Node *T){//print l rt r
if (NULL==T)return ;
InOrder(T->l);
printf("%d ",T->x);
InOrder(T->r);
}
void Free(){FreeTree(rt);}
void FreeTree(Node *T){
if (NULL==T)return ;
FreeTree(T->l);
FreeTree(T->r);
free(T);
}
private:
Node *rt;//root
};
int main(){
freopen("fuck.in","r",stdin);
int n,x,op,a=0,b=0;
scanf("%d",&n);
AVLTree T; T.Init();
for (;n--;){
scanf("%d%d",&op,&x);
//if pets>people put pets into the tree
//else put people into the tree
if (op==0){//come a pet
a++;
if (a>b)T.Insert(x);//more pet
else T.GetPet(x);//more people
}else{//come a person
b++;
if (a<b)T.Insert(x);//more people
else T.GetPet(x);//more pet
}
}
printf("%d\n",ans%BASE);
T.Free();
return 0;
}
Splay
#include<cstdio>
#include<algorithm>
using namespace std;
struct Node{
int key;//size
Node *l,*r,*f;//left,right,father
};
class SplayTree{
public:
void Init(){rt=NULL;}
void Zag(Node *x){//left rotate
Node *y=x->f;//y is the father of x
y->r = x->l;
if (x->l)x->l->f = y;//if x has left child
x->f =y->f;
if (y->f){//y is not root
if (y==y->f->l)y->f->l=x;//y if left child
else y->f->r=x;//y is right child
}
y->f=x; x->l=y;
}
void Zig(Node *x){//right rotate
Node *y=x->f;//y is the father of x
y->l = x->r;
if (x->r)x->r->f=y;
x->f = y->f;
if (y->f){
if (y==y->f->l)y->f->l=x;
else y->f->r=x;
}
y->f=x;
x->r=y;
}
void Splay(Node *x){
while (x->f){
Node *p=x->f;
if (!p->f){
if (x==p->l)Zig(x);
else Zag(x);
}else if (x==p->l){
if (p==p->f->l){Zig(p);Zig(x);}
else {Zig(x);Zag(x);}
}else {//x==p->r
if (p==p->f->r){Zag(p);Zag(x);}
else {Zag(x);Zig(x);}
}
}
rt=x;
}
Node *Find(int x){
Node *T=rt;
while (T){
if (T->key==x){Splay(T);return T;}
else if (x<T->key)T=T->l;
else T=T->r;
}
return T;
}
void Insert(int x){
Node *T=rt,*fa=NULL;
while (T){
fa=T;
if (x<T->key)T=T->l;
else if(x>T->key)T=T->r;
else return ;//two the same keys
}
T=(Node*)malloc(sizeof(Node));
T->key=x;
T->l=T->r=NULL;
T->f=fa;
if (fa){
if (fa->key>x)fa->l=T;
else fa->r=T;
}
Splay(T);
}
void Delete(int x){
Node *T=Find(x);
if (NULL==T)return ;//error
rt=Join(T->l,T->r);
}
Node *Maxnum(Node *t){
Node *T=t;
while (T->r)T=T->r;
Splay(T);
return T;
}
Node *Minnum(Node *t){
Node *T=t;
while (T->l)T=T->l;
Splay(T);
return T;
}
Node *Last(int x){
Node *T=Find(x);
T=T->l;
return (Maxnum(T));
}
Node *Next(int x){
Node *T=Find(x);
T=T->r;
return (Minnum(T));
}
Node *Join(Node *t1,Node *t2){
if (NULL==t1)return t2;
if (NULL==t2)return t1;
Node *T=Maxnum(t1);
T->l=t2;
return T;
}
void Split(int x,Node *&t1,Node *&t2){
Node *T=Find(x);
t1=T->l; t2=T->r;
}
void Inorder(Node *T){
if (NULL==T)return ;
Inorder(T->l);
printf("%d->",T->key);
Inorder(T->r);
}
void _Delete(){Delete(rt);}
void Delete(Node *T){
if (NULL==T)return ;
Delete(T->l);
Delete(T->r);
free(T);
}
private:
Node *rt;//root
};
Splay带区间翻转标记
节点维护siz域用于查找当前第k个位置上的数,翻转打标记。
对于splay,翻转[L,R]只要把第L-1个位置转到根,第R+1个位置转到root->R的位置,对root->R->L打翻转标记。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100005;
const int INF = 0x3f3f3f3f;
struct node{
int key,siz;
bool fg;
node *ch[2],*fa;
};
node* root=NULL;
int n,m;
node *new_node(node *fa,int key){
node *t = new node;
t->ch[0]=t->ch[1] = NULL;
t->fa = fa;
t->key = key; t->siz = 1;
t->fg = 0;
return t;
}
#define siz(t) (t == NULL ? 0 : t->siz)
void pushup(node *t){
t->siz = siz(t->ch[0])+siz(t->ch[1]) + 1;
}
void pushdown(node *t){
if(!t->fg)return;
t->fg=0;
swap(t->ch[0],t->ch[1]);
if(t->ch[0]) t->ch[0]->fg^=1;
if(t->ch[1]) t->ch[1]->fg^=1;
}
node *build(node* fa,int L,int R){
if(L>R)return NULL;
int mid=(L+R)>>1,key=mid-1;
node *t=new_node(fa,key);
t->ch[0]=build(t,L,mid-1);
t->ch[1]=build(t,mid+1,R);
pushup(t);
return t;
}
void order(node *t){
if(t == NULL)return;
pushdown(t);
order(t->ch[0]);
if(t->key >=1 && t->key <= n) printf("%d ",t->key);
order(t->ch[1]);
}
void rotate(node *x,bool d){
node *y=x->fa;
y->ch[!d]=x->ch[d];
if(x->ch[d]) x->ch[d]->fa=y;
x->fa = y->fa;
if(y->fa){
if(y == y->fa->ch[0])
y->fa->ch[0] = x;
else
y->fa->ch[1] = x;
}
x->ch[d] = y;y->fa=x;
pushup(y);pushup(x);
}
void splay(node *x,node *tar){
while( x->fa != tar){
node *y=x->fa;
if( x == y->ch[0]){
if(y->fa != tar && y == y->fa->ch[0])
rotate(y,1);
rotate(x,1);
}else{
if(y->fa != tar && y == y->fa->ch[1])
rotate(y,0);
rotate(x,0);
}
}
if( tar == NULL ) root=x;
}
node *find_kth(node *x,int kth){
pushdown(x);
int siz=siz(x->ch[0]);
if( siz+1 == kth ) return x;
if( kth <= siz )return find_kth(x->ch[0],kth);
return find_kth(x->ch[1],kth-siz-1);
}
void rev(int L,int R){
node *x=find_kth(root,L),*y=find_kth(root,R+2);
splay(x,NULL);
splay(y,root);
y->ch[0]->fg^=1;
}
int main(){
freopen("input.txt","r",stdin);
freopen("output1.txt","w",stdout);
scanf("%d%d",&n,&m);
root = build(NULL,1,n+2);
for(int L,R,i=1;i<=m;i++){
scanf("%d%d",&L,&R);
rev(L,R);
}
order(root);puts("");
}
左偏树
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 1e5 + 5;
struct node{
int l,r,dis,key;
} tree[MAXN];
int far[MAXN];
int Find(int x) {
if(far[x] == x) return x;
return far[x] = Find(far[x]);
}
int merge(int a,int b){
if(!a) return b;
if(!b) return a;
if(tree[a].key < tree[b].key) swap(a, b);//大堆
tree[a].r = merge(tree[a].r,b);
far[tree[a].r] = a;//并查
if(tree[tree[a].l].dis < tree[tree[a].r].dis) swap(tree[a].l,tree[a].r);
if(tree[a].r)tree[a].dis = tree[tree[a].r].dis + 1;
else tree[a].dis = 0;
return a;
}
int pop(int a){
int l = tree[a].l;
int r = tree[a].r;
far[l] = l;//因为要暂时删掉根,所以左右子树先作为根
far[r] = r;
tree[a].l = tree[a].r = tree[a].dis = 0;
return merge(l,r);
}
int main(){
int N, M;
while(cin >> N){
for(int i = 1; i <= N; i++){
int x;
far[i] = i;
scanf("%d", &x);
tree[i].key = x;
tree[i].l = tree[i].r = tree[i].dis = 0;
}
cin >> M;
while(M--) {
int x, y;
scanf("%d%d", &x, &y);
x = Find(x);
y = Find(y);
if(x == y) {
printf("-1\n");
} else {
int ra = pop(x);
tree[x].key /= 2;
ra = merge(ra, x);
int rb = pop(y);
tree[y].key /= 2;
rb = merge(rb, y);
x = merge(ra, rb);
printf("%d\n", tree[x].key);
}
}
}
return 0;
}
分块
int n,m,num,belong[maxn],block,l[maxn],r[maxn],a[maxn],p[maxn];
void build(){
block=sqrt(n);
num=n/block;
if(n%block) num++;
for(int i=1;i<=num;i++) l[i]=(i-1)*block+1,r[i]=i*block;
r[num]=n;
for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
}
RMQ
inline void initrmq(int n,int b[]){
mm[0] = -1;
for(int i=1;i<=n;i++){
mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];//log
f[i][0]=b[i];
}
for(int j=1;j<=mm[n];j++)
for(int i=1;i+(1<<j)-1<=n;i++)
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
inline int rmqmin(int x, int y){
int k=mm[y-x+1];
return min(f[x][k],f[y-(1<<k)+1][k]);
}
树状数组逆序对
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 20005;
int T[maxn];
int a[maxn];
int b[maxn];
inline int lowbit(int x){
return x&(-x);
}
inline ll ask(int x){
ll ret = 0;
while(x){
ret += T[x];
x -= lowbit(x);
}
return ret;
}
inline void add(int x,int d){
while(x<=20000){
T[x]+=d;
x+=lowbit(x);
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(a+1,a+1+n);
int cnt = unique(a+1,a+1+n)-(a+1);
for(int i=1;i<=n;i++)
b[i] = lower_bound(a+1,a+1+n,b[i])-a;
memset(T,0,sizeof T);
ll res = 0;
for(int i=n;i>=1;i--){
res += ask(b[i]-1);
add(b[i],1);
}
printf("%lld\n",res);
return 0;
}
划分树求区间K大
query求区间第K大。
#include <bits/stdc++.h>
using namespace std;
template<typename T> inline void read(T &x){
x=0;T f=1;char ch;do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');do x=x*10+ch-'0',ch=getchar();while(ch<='9'&&ch>='0');x*=f;
}
template<typename A,typename B> inline void read(A&x,B&y){read(x);read(y);}
template<typename A,typename B,typename C> inline void read(A&x,B&y,C&z){read(x);read(y);read(z);}
template<typename A,typename B,typename C,typename D> inline void read(A&x,B&y,C&z,D&w){read(x);read(y);read(z);read(w);}
int n,m,ans1,ans2;
const int maxn = 400000 + 5;
int a[maxn];
int toleft[25][maxn];
int T[25][maxn];
void build(int l,int r,int dep){
if(l == r) return;
int mid = (l+r)/2;
int same = mid - l + 1;
for(int i=l;i<=r;i++)
if(T[dep][i] < a[mid])
same--;
int ls = l, rs = mid + 1;
for(int i=l;i<=r;i++){
int flag = 0;
if((T[dep][i]==a[mid]&&same>0)||(T[dep][i]<a[mid])){
T[dep+1][ls] = T[dep][i];
ls++;
flag = 1;
if(T[dep][i] == a[mid])
same--;
} else {
T[dep+1][rs] = T[dep][i];
rs++;
}
toleft[dep][i] = toleft[dep][i-1] + flag;
}
build(l,mid,dep+1);
build(mid+1,r,dep+1);
}
int query(int l,int r,int k,int L,int R,int dep){
if(l == r)
return T[dep][l];
int mid = (L+R)/2;
int x = toleft[dep][l-1] - toleft[dep][L-1];
int y = toleft[dep][r] - toleft[dep][L-1];
int ry = r - L - y;
int cnt = y - x;
int rx = l - L - x;
if(cnt >= k)
return query(L + x, L + y - 1, k, L, mid, dep + 1);
else
return query(mid + rx + 1, mid + 1 + ry, k - cnt, mid + 1, R, dep + 1);
}
int main(){
read(n,m);
for(int i=1;i<=n;i++){
read(a[i]);
T[0][i]=a[i];
}
sort(a+1,a+1+n);
build(1,n,0);
int l,r,x,k1,k2;
for(int i=1;i<=m;i++){
read(l,r,x);read(k1,k2);
if(k1 > r - l + 1) ans1 = -1;
else {
ans1 = query(l,r,k1,1,n,0);
}
if(k2>r-l+1) ans2 = -1;
else {
int Left=1,Right=r-l+1;
int kid;
while(Left<=Right){
int mid = (Left+Right)/2;
int tmp = query(l,r,mid,1,n,0);
if(tmp>x){
Right=mid-1;
kid = mid;
}
else Left = mid + 1;
}
int tt = kid + k2 - 1;
if( kid + k2 - 1 > r - l + 1) ans2 = -1;
else ans2 = query(l,r,kid + k2 - 1,1,n,0);
}
if(ans1>x) ans1 = -1;
if(ans2<x) ans2 = -1;
printf("%d %d\n",ans1,ans2);
}
return 0;
}
字符串
kmp
#include <bits/stdc++.h>
using namespace std;
const int maxn=100000+100;
char s[maxn*2];
int nxt[maxn];
char s1[maxn];
char s2[maxn];
//当位置 i !=位置j ,nxt[] 指向的是一个位置 k=nxt[j]
//位置k 是不是和i 相等,不知道,但是位置i 的前面一定等一位置k 的前面到开始的字符,前缀思想
//同时nxt[0]=-1,nxt 下标从1 开始,对应模式串下标从0 开始
void init(char x[], int m, int nxt[]) {
int i, j;
j = nxt[0] = -1;
i = 0;
while (i < m) {
while (-1 != j && x[i] != x[j])
j = nxt[j];
nxt[++i] = ++j;
}
}
/*
* kmpnxt[]的意思:nxt'[i]=nxt[nxt[...[nxt[i]]]] (直到nxt'[i]<0或者
x[nxt'[i]]!=x[i])
* 这样的预处理可以快一些
*/
void init2(char x[], int m, int kmpnxt[]) {
int i, j;
j = kmpnxt[0] = -1;
i = 0;
while (i < m) {
while (-1 != j && x[i] != x[j])
j = kmpnxt[j];
if (x[++i] == x[++j])
kmpnxt[i] = kmpnxt[j];
else
kmpnxt[i] = j;
}
}
/*
* 返回x在y中出现的次数,可以重叠
*/
inline bool kmp(char x[], int m, char y[], int n) { // x 是模式串,y 是主串
int i, j;
int ans = 0;
// init2(x,m,nxt);
init(x, m, nxt);
i = j = 0;
while (i < n) {
while (-1 != j && y[i] != x[j])
j = nxt[j];
i++;
j++;
if (j >= m) {
return true;
}
}
return false;
}
int main() {
scanf("%s",s);
int len =(int )strlen(s);
init2(s,len,nxt);
for(int i=0;i<=len;i++) {
printf("i: %d val: %d\n",i,nxt[i]);
}
return 0;
}
利用kmp中的nxt数组,求字符串的循环节
//总结一下,如果对于next数组中的 i, 符合 i % ( i - next[i] ) == 0 && next[i] != 0 , //则说明字符串循环
//循环节长度为:i - next[i]
//循环次数为:i / ( i - next[i] )
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=1000000+100;
char s[maxn];
int nxt[maxn];
void init(char x[], int m, int nxt[]) {
int i, j;
j = nxt[0] = -1;
i = 0;
while (i < m) {
while (-1 != j && x[i] != x[j])
j = nxt[j];
nxt[++i] = ++j;
}
}
int main() {
while(scanf("%s",s)!=EOF) {
if(s[0]=='.') break;
int len=strlen(s);
init(s,len,nxt);
int ss=len-nxt[len];
if(len%ss==0&&nxt[len]!=0) {
printf("%d\n",len/ss);
}else printf("1\n");
}
return 0;
}
扩展kmp
void getnext(char x[],int m){
nextt[0]=m;
int j=0;
while(j+1<m && x[j]==x[j+1]) j++;
nextt[1]=j;
int k=1;
for(int i=2;i<m;i++){
int p=nextt[k]+k-1;
int L=nextt[i-k];
if(i+L<p+1) nextt[i]=L;
else{
j=max(0,p-i+1);
while(i+j<m && x[i+j]==x[j]) j++;
nextt[i]=j;
k=i;
}
}
}
void ekmp(char x[],int m,char y[],int n){
int j=0;
while(j<n && j<m && x[j]==y[j]) j++;
extend[0]=j;
int k=0;
for(int i=1;i<=n;i++){
int p=extend[k]+k-1;
int L=nextt[i-k];
if(i+L<p+1) extend[i]=L;
else{
j=max(0,p-i+1);
while(i+j<n && j<m && y[i+j]==x[j]) j++;
extend[i]=j;
k=i;
}
}
}
Manacher算法
#include <bits/stdc++.h>
using namespace std;
const int maxn=110010;
char Ma[maxn*2];
int Mp[maxn*2];
//时间复杂度:O(n) 开两倍数组
inline int Manacher(char s[],int len) {
int l=0;
Ma[l++]='$';
Ma[l++]='#';
for(int i=0;i<len;i++) {
Ma[l++]=s[i];
Ma[l++]='#';
}
Ma[l]='\0';
int mx=0,id=0;
int ans=-1;
for(int i=0;i<l;i++) {
Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++;
if(i+Mp[i]>mx) {
id=i;
mx=i+Mp[i];
}
ans=max(ans,Mp[i]-1);
}
return ans;
}
char s[maxn];
int main() {
while(scanf("%s",s)!=EOF) {
getchar();
int len=(int)strlen(s);
int ans=Manacher(s, len);
printf("%d\n",ans);
}
return 0;
}
字典树
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
const int maxn=50010;
int vis[maxn];
char s1[50],s2[50];
struct tree {
tree *child[26];
int flag;
tree() {
flag=0;
memset(child,0,sizeof(child));
}
};
tree *root;
char str[maxn][26];
void insert(char *source) {
tree *cur,*tmp;
int len=strlen(source);
cur=root;
for(int i=0; i<len; i++) {
if(cur->child[source[i]-'a']!=0)
cur=cur->child[source[i]-'a'];
else {
tmp=new tree;
cur->child[source[i]-'a']=tmp;
cur=tmp;
}
}
cur->flag=1;
}
int find(char *source) {
tree *cur;
int len=strlen(source);
cur=root;
for(int i=0; i<len; i++) {
if(cur->child[source[i]-'a']!=0)
cur=cur->child[source[i]-'a'];
else return 0;
}
return cur->flag;
}
int main() {
// freopen("input.txt","r",stdin);
root=new tree;
int k=0;
while(scanf("%s",str[k])!=EOF) {
insert(str[k]);
k++;
}
for(int i=0; i<k; i++) {
int len=strlen(str[i]);
vis[i]=0;
if(len==1) continue;
for(int x=1; x<len; x++) {
int j,j2;
for(j=0; j<x; j++)
s1[j]=str[i][j];
s1[j]='\0';
for(j2=0; j<len; j++,j2++)
s2[j2]=str[i][j];
s2[j2]='\0';
if(find(s1)&&find(s2)) {
vis[i]=1;
break;
}
}
}
for(int i=0; i<k; i++)
if(vis[i])
printf("%s\n",str[i]);
return 0;
}
AC自动机
//======================
// HDU 2222
// 求目标串中出现了几个模式串
//====================
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
struct Trie {
int next[500010][26],fail[500010],end[500010];
int root,L;
int newnode() {
for(int i = 0; i < 26; i++)
next[L][i] = -1;
end[L++] = 0;
return L-1;
}
void init() {
L = 0;
root = newnode();
}
void insert(char buf[]) {
int len = strlen(buf);
int now = root;
for(int i = 0; i < len; i++) {
if(next[now][buf[i]-'a'] == -1)
next[now][buf[i]-'a'] = newnode();
now = next[now][buf[i]-'a'];
}
end[now]++;
}
void build() {
queue<int>Q;
fail[root] = root;
for(int i = 0; i < 26; i++)
if(next[root][i] == -1)
next[root][i] = root;
else {
fail[next[root][i]] = root;
Q.push(next[root][i]);
}
while( !Q.empty() ) {
int now = Q.front();
Q.pop();
for(int i = 0; i < 26; i++)
if(next[now][i] == -1)
next[now][i] = next[fail[now]][i];
else {
fail[next[now][i]]=next[fail[now]][i];
Q.push(next[now][i]);
}
}
}
int query(char buf[]) {
int len = strlen(buf);
int now = root;
int res = 0;
for(int i = 0; i < len; i++) {
now = next[now][buf[i]-'a'];
int temp = now;
while( temp != root ) {
res += end[temp];
end[temp] = 0;
temp = fail[temp];
}
}
return res;
}
void debug() {
for(int i = 0; i < L; i++) {
printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
for(int j = 0; j < 26; j++)
printf("%2d",next[i][j]);
printf("]\n");
}
}
};
char buf[1000010];
Trie ac;
int main() {
int T;
int n;
scanf("%d",&T);
while( T-- ) {
scanf("%d",&n);
ac.init();
for(int i = 0; i < n; i++) {
scanf("%s",buf);
ac.insert(buf);
}
ac.build();
scanf("%s",buf);
printf("%d\n",ac.query(buf));
}
return 0;
}
#include <cstring>
#include <iostream>
#include <queue>
#include <cstdio>
using namespace std;
char st[202];
char buf[10086];
struct Tire{
int nxt[205*502][128],fail[205*502],end[205*502];
int root,top;
int newnode(){
for(int i=0;i<128;i++)
nxt[top][i]=-1;
end[top++]=-1;
return top-1;
}
void init(){
top=0;
root=newnode();
}
void Insert(char s[],int id){
int len = strlen(s);
int now = root;
for(int i=0;i<len;i++){
if(nxt[now][s[i]]==-1)
nxt[now][s[i]]=newnode();
now=nxt[now][s[i]];
}
end[now]=id;
}
void build_fail_pointer(){
queue<int> Q;
fail[root]=root;
for(int i = 0;i < 128;i++)
if(nxt[root][i] == -1)
nxt[root][i] = root;
else
{
fail[nxt[root][i]] = root;
Q.push(nxt[root][i]);
}
while(!Q.empty()){
int now = Q.front();
Q.pop();
for(int i=0;i<128;i++){
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else{
fail[nxt[now][i]] = nxt[fail[now]][i];
Q.push(nxt[now][i]);
}
}
}
}
bool used[510];
bool query(char buf[],int n,int id){
int len = strlen(buf);
int now = root;
memset(used,false,sizeof(used));
bool flag = false;
for(int i = 0;i < len;i++)
{
now = nxt[now][buf[i]];
int temp = now;
while(temp != root)
{
if(end[temp] != -1)
{
used[end[temp]] = true;
flag = true;
}
temp = fail[temp];
}
}
if(!flag)return false;
printf("web %d:",id);
for(int i = 1;i <= n;i++)
if(used[i])
printf(" %d",i);
printf("\n");
return true;
}
}T;
int main(){
int n,m;
scanf("%d",&n);
T.init();
getchar();
for(int i=1;i<=n;i++){
gets(st);
T.Insert(st,i);
}
T.build_fail_pointer();
scanf("%d",&m);
getchar();
int ans=0;
for(int i=1;i<=m;i++){
gets(buf);
if(T.query(buf,n,i))
ans++;
}
printf("total: %d\n",ans);
return 0;
}
后缀数组
#include <bits/stdc++.h>
using namespace std;
const int maxn=200000+100;
int wa[maxn], wb[maxn], wv[maxn], Ws[maxn];
int Rank[maxn], height[maxn];
int sa[maxn], r[maxn];
char str[maxn];
int cmp(int* r, int a, int b, int l)
{
return r[a] == r[b] && r[a + l] == r[b + l];
}
//将原串利用ascii 码复制到r 数组中,r[len]=0;
//m 是原串中ascii 码最大值加一,合理设值,会影响通排序的基数
//复杂度 nlogn
//n+1
void da(int* r, int* sa, int n, int m)
{
int i, j, p, *x = wa, *y = wb, *t;
for (i = 0; i < m; i++)
Ws[i] = 0;
for (i = 0; i < n; i++)
Ws[x[i] = r[i]]++;
for (i = 1; i < m; i++)
Ws[i] += Ws[i - 1];
for (i = n - 1; i >= 0; i--)
sa[--Ws[x[i]]] = i;
for (p = 1, j = 1; p < n; j *= 2, m = p) {
for (p = 0, i = n - j; i < n; i++)
y[p++] = i;
for (i = 0; i < n; i++)
if (sa[i] >= j)
y[p++] = sa[i] - j;
for (i = 0; i < n; i++)
wv[i] = x[y[i]];
for (i = 0; i < m; i++)
Ws[i] = 0;
for (i = 0; i < n; i++)
Ws[wv[i]]++;
for (i = 1; i < m; i++)
Ws[i] += Ws[i - 1];
for (i = n - 1; i >= 0; i--)
sa[--Ws[wv[i]]] = y[i];
for (t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i++)
x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
}
}
//n
//复杂度:nlogn
void calheight(int* r, int* sa, int n)
{
int i, j, k = 0;
for (i = 1; i <= n; i++)
Rank[sa[i]] = i;
for (i = 0; i < n; height[Rank[i++]] = k)
for (k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; k++)
;
}
int main()
{
//freopen("in.txt", "r", stdin);
while(scanf("%s",str)!=EOF) {
int len=(int)(strlen(str));
str[len]='#';
scanf("%s",str+len+1);
int len2=(int)strlen(str);
for(int i=0;i<len2;i++) r[i]=str[i];
r[len2]=0;
da(r, sa, len2+1, 180);
calheight(r, sa, len2);
int ans=0;
// for(int i=1;i<len2;i++) {
// cout<<height[i]<<endl;
// }
for(int i=2;i<len2;i++) {
if(ans<height[i])
if((sa[i]<len&&sa[i-1]>len)||(sa[i]>len&&sa[i-1]<len))
ans=height[i];
//cout<<ans<<endl;
}
printf("%d\n",ans);
}
return 0;
}
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int MAXN=20010;
/*
*suffix array
*倍增算法 O(n*logn)
*待排序数组长度为n,放在0~n-1中,在最后面补一个0
*build_sa( ,n+1, );//注意是n+1;
*getHeight(,n);
*例如:
*n = 8;
*num[] = { 1, 1, 2, 1, 1, 1, 1, 2, $ };注意num最后一位为0,其他大于0
*rank[] = { 4, 6, 8, 1, 2, 3, 5, 7, 0 };rank[0~n-1]为有效值,rank[n]必定为0无效值
*sa[] = { 8, 3, 4, 5, 0, 6, 1, 7, 2 };sa[1~n]为有效值,sa[0]必定为n是无效值
*height[]= { 0, 0, 3, 2, 3, 1, 2, 0, 1 };height[2~n]为有效值
*
*/
int sa[MAXN];//SA数组,表示将S的n个后缀从小到大排序后把排好序的
//的后缀的开头位置顺次放入SA中
int t1[MAXN],t2[MAXN],c[MAXN];//求SA数组需要的中间变量,不需要赋值
int rank[MAXN],height[MAXN];
//待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m,
//除s[n-1]外的所有s[i]都大于0,r[n-1]=0
//函数结束以后结果放在sa数组中
void build_sa(int s[],int n,int m)
{
int i,j,p,*x=t1,*y=t2;
//第一轮基数排序,如果s的最大值很大,可改为快速排序
for(i=0;i<m;i++)c[i]=0;
for(i=0;i<n;i++)c[x[i]=s[i]]++;
for(i=1;i<m;i++)c[i]+=c[i-1];
for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
for(j=1;j<=n;j<<=1)
{
p=0;
//直接利用sa数组排序第二关键字
for(i=n-j;i<n;i++)y[p++]=i;//后面的j个数第二关键字为空的最小
for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i<m;i++)c[i]=0;
for(i=0;i<n;i++)c[x[y[i]]]++;
for(i=1;i<m;i++)c[i]+=c[i-1];
for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p=1;x[sa[0]]=0;
for(i=1;i<n;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
if(p>=n)break;
m=p;//下次基数排序的最大值
}
}
void getHeight(int s[],int n)
{
int i,j,k=0;
for(i=0;i<=n;i++)rank[sa[i]]=i;
for(i=0;i<n;i++)
{
if(k)k--;
j=sa[rank[i]-1];
while(s[i+k]==s[j+k])k++;
height[rank[i]]=k;
}
}
int s[MAXN];
bool check(int n,int k)
{
int Max=sa[1],Min=sa[1];
for(int i=2;i<=n;i++)
{
if(height[i]<k)Max=Min=sa[i];
else
{
if(sa[i]<Min)Min=sa[i];
if(sa[i]>Max)Max=sa[i];
if(Max-Min>k)return true;
}
}
return false;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n;
while(scanf("%d",&n)==1 && n)
{
for(int i=0;i<n;i++)scanf("%d",&s[i]);
for(int i=n-1;i>0;i--)s[i]=s[i]-s[i-1]+90;
n--;//减少一个长度
for(int i=0;i<n;i++)s[i]=s[i+1];
s[n]=0;
build_sa(s,n+1,200);
getHeight(s,n);
int ans=-1;
int l=1,r=n/2;
while(l<=r)
{
int mid=(l+r)/2;
if(check(n,mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
if(ans<4)printf("0\n");
else printf("%d\n",ans+1);
}
return 0;
}
后缀自动机
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10005;
char str[N];
struct SAM {
struct Node {
int ch[26];
int f, len;
void init() {
f = -1, len = 0;
memset(ch, 0xff, sizeof (ch));
}
};
Node sn[N<<1];
int idx, last;
void init() {
idx = last = 0;
sn[idx++].init();
}
int newnode() {
sn[idx].init();
return idx++;
}
void add(int c) {
int end = newnode();
int tmp = last;
sn[end].len = sn[last].len + 1;
for ( ; tmp != -1 && sn[tmp].ch[c] == -1; tmp = sn[tmp].f) {
sn[tmp].ch[c] = end;
}
if (tmp == -1) sn[end].f = 0; // 所有的上一轮可接受点都没有指向字符c 的孩子节点
else {
int nxt = sn[tmp].ch[c];
if (sn[tmp].len + 1 == sn[nxt].len) sn[end].f = nxt; // 如果可接受点有向c 的转移,且长度只加1 ,那么该孩子可以替代当前的end ,并且end 的双亲指向该孩子
else {
int np = newnode();
sn[np] = sn[nxt];
sn[np].len = sn[tmp].len + 1;
sn[end].f = sn[nxt].f = np;
for (; tmp != -1 && sn[tmp].ch[c] == nxt; tmp = sn[tmp].f) {
sn[tmp].ch[c] = np;
}
}
}
last = end;
}
};
SAM sam;
char s[10010];
int main() {
int t;scanf("%d",&t);
getchar();
while(t--) {
scanf("%s",s);
int len=strlen(s);
sam.init();
for(int i=0;i<len*2;i++){
sam.add(s[i%len]-'a');
}
int p=0;
for(int i=0;i<len;i++) {
for(int j=0;j<26;j++) {
if(sam.sn[p].ch[j]!=-1) {
p=sam.sn[p].ch[j];
break;
}
}
}
printf("%d\n",sam.sn[p].len-len+1);
}
return 0;
}
回文串自动机
const int MAXN = 100005 ;
const int N = 26 ;
struct Palindromic_Tree {
int next[MAXN][N] ;//next 指针,next 指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[MAXN] ;//fail 指针,失配后跳转到fail 指针指向的节点
int cnt[MAXN] ;
int num[MAXN] ;
int len[MAXN] ;//len[i] 表示节点i 表示的回文串的长度
int S[MAXN] ;//存放添加的字符
int last ;//指向上一个字符所在的节点,方便下一次add
int n ;//字符数组指针
int p ;//节点指针
int newnode ( int l ) {//新建节点
for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
cnt[p] = 0 ;
num[p] = 0 ;
len[p] = l ;
return p ++ ;
}
void init () {//初始化
p = 0 ;
newnode ( 0 ) ;
newnode ( -1 ) ;
last = 0 ;
n = 0 ;
S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
fail[0] = 1 ;
}
int get_fail ( int x ) {//和KMP 一样,失配后找一个尽量最长的
while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
return x ;
}
void add ( int c ) {
c -= 'a' ;
S[++ n] = c ;
int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
int now = newnode ( len[cur] + 2 ) ;//新建节点
fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC 自动机一样建立fail 指针,以便失配后跳转
next[cur][c] = now ;
num[now] = num[fail[now]] + 1 ;
}
last = next[cur][c] ;
cnt[last] ++ ;
}
void count () {
for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
//父亲累加儿子的cnt ,因为如果fail[v]=u ,则u 一定是v 的子回文串!
}
} ;
动态规划
有依赖的背包问题
一般有主件和附件之分,附件依赖主件。
可以先对所有的主件做DP处理,再对整体做DP。
多重背包通用模板(单调队列)
int f[N], va[N], vb[N];//MAX_V
void pack(int V,int v,int w,int n){
if (n==0 || v==0) return;
if (n==1){ // 01 背包
for (int i=V;i>=v;--i) f[i] = max(f[i], f[i-v] + w);
return;
}
if (n*v >= V-v+1){ // 多重背包 (n >= V / v)
for (int i = v; i <= V; ++i) f[i] = max(f[i], f[i-v] + w);
return;
}
for (int j = 0 ; j < v ; ++j ){
int *pb = va, *pe = va - 1;
int *qb = vb, *qe = vb - 1;
for (int k = j, i = 0;k <= V; k += v, ++i){
if (pe == pb+n){
if(*pb == *qb) ++qb;
++pb;
}
int tt = f[k] - i * w;
*++pe = tt;
while (qe >= qb && *qe < tt) --qe;
*++qe = tt;
f[k] = *qb + i * w;
}
}
}
//主程序调用
memset(f,0,sizeof(f)); //pack
for (int i=1;i<=n;i++)
pack(cash,a[i].v,a[i].w,a[i].n);
int ans = 0; //getAns
for (int i=0;i<=cash;i++) ans=max(ans,f[i]);
printf("%d\n",ans);
小价值大重量背包
for(int i = 0; i < n; i++) {
for(int j = MAXV - 1; j >= v[i] ; j--) {
f[j] = min(f[j], f[j - v[i]] + w[i]);
}
}
int ans = -1;
for(int i = MAXV - 1; i >= 0; i--) {
if (f[i] <= V) {ans = i; break;}
}
if (ans != -1) printf("%d\n", ans);
else printf("No solution\n");
数位DP
LL n, dp[25][3];
//dp[i][j] : 长度为 i ,状态为 j
int digit[25];
//nstatus: 0 :不含 49 , 1 :不含 49 但末尾是 4 , 2 :含 49
LL DFS(int pos, int status, int limit){
if(pos <= 0) // 如果到了已经枚举了最后一位,并且在枚举的过程中有 49 序列出现
return status==2;//注意是 ==
if(!limit && dp[pos][status]!=-1) //对于有限制的询问我们是不能够记忆化的
return dp[pos][status];
LL ans = 0;
int End = limit?digit[pos]:9; // 确定这一位的上限是多少
for(int i = 0; i <= End; i++){ // 每一位有这么多的选择
int nstatus = status; // 有点 else s = statu 的意思
if(status==0 && i==4)//高位不含 49 ,并且末尾不是 4 ,现在末尾添 4 返回 1 状态
nstatus = 1;
else if(status==1 && i!=4 && i!=9)//高位不含 49 ,且末尾是 4 ,现在末尾添加的不是 4 返回 0 状态
nstatus = 0;
else if(status==1 && i==9)//高位不含 49 ,且末尾是 4 ,现在末尾添加 9 返回 2 状态
nstatus = 2;
ans+=DFS(pos-1, nstatus, limit && i==End);
}
if(!limit)
dp[pos][status]=ans;
return ans;
}
斜率优化DP
一般形式,$DP_{n}=min(DP_{j}+M+F_{(i,j)}$.
需要化成$\frac {Y(j) - Y(k)} { X(j) - X(k)} < f(i)$的形式。
f=r=0;
Q[r++]=0;
for(int i=1;i<=n;i++){
while(f+1<r && getup(Q[f+1],Q[f]) <= sum[i]*getdown(Q[f+1],Q[f]) )
f++;
dp[i]=getDP(i,Q[f]);
while(f+1<r && getup(i,Q[r-1])*getdown(Q[r-1],Q[r-2]) <= getup(Q[r-1],Q[r-2])*getdown(i,Q[r-1]))
r--;
Q[r++]=i;
}
树上的话,加个tmp记录队列尾端的更新值在回溯的时候恢复。
void dfs(int id,int fa,int f,int r){
dp[id]=INIT;
int tmp=-1;
if(id!=1){
while(f+1<r && getup(Q[f+1],Q[f]) <= dist[id]*getdown(Q[f+1],Q[f]) )
f++;
dp[id]=min(dp[id],getDP(id,Q[f]));
while(f+1<r && getup(id,Q[r-1])*getdown(Q[r-1],Q[r-2]) <= getup(Q[r-1],Q[r-2])*getdown(id,Q[r-1]))
r--;
tmp=Q[r];
Q[r++]=id;
}
ans=max(ans,dp[id]);
for(auto edg:e[id]){
if(edg.v==fa) continue;
dist[edg.v]=dist[id]+edg.w;
dfs(edg.v,id,f,r);
}
if(tmp!=-1)
Q[r]=tmp;
}
状态压缩+矩阵优化
求NxM的棋盘可以多少种放骨牌的方案,M较小。
ll n,m;
matrix base;
int dfs(int dep,int pre,int now){
if(dep> m) return 0;
if(dep==m){
base[pre][now]++;
return 0;
}
dfs(dep+1,pre<<1,now<<1|1);
dfs(dep+1,pre<<1|1,now<<1);
dfs(dep+2,pre<<2,now<<2);
}
int main(){
scanf("%lld%lld",&n,&m);
memset(base.a,0,sizeof base.a);
dfs(0,0,0);
base = base ^ (n + 1);
printf("%lld\n",base[0][(1<<m)-1]);
return 0;
}
矩阵优化DP递推式
1.若$f_{i,j}=f_{i-1,j\pm 1}+f_{i-2,j}$。
定义矩阵列:$j \in [0,n-1]$列表示$f_{i-1,j}$,$j \in [n,2n-1]$列表示$f_{i-2,j}$。
$j \in [0,n-1]$行表示$f_{i,j}$,$j \in [n,2n-1]$行表示$f_{i-1,j}$。
从$f_{i-2} \rightarrow f_{i}$转移斜对角线为1。从$f_{i-1} \rightarrow f_{i-1}$斜对角线为1。
从$f_{i-1} \rightarrow f_{i}$有$base[i][i-1~i+1]$为1。
那么矩阵就可以实现转移了。
2.求$\sum C_{nk}^{ik+r}$。
设矩阵表示模k余r的和。$f[i][j]=f[i-1][j]+f[i-1][(j-1+k)% k]$。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 105;
int n,p,k,r,maxsize;
int P;
struct matrix{
ll a[N][N];
int row,col;
matrix():row(N),col(N){memset(a,0,sizeof(a));}
matrix(int x,int y):row(x),col(y){memset(a,0,sizeof(a));}
ll* operator [] (int x){return a[x];}
matrix operator * (matrix x){
matrix tmp ;
for (int i=0;i<maxsize;i++)
for (int j=0;j<maxsize;j++){
tmp[i][j]=0;
for (int k=0;k<maxsize;k++)
tmp[i][j]=(tmp[i][j]+a[i][k]*x[k][j])%P;
}
return tmp;
}
void operator *= (matrix x){*this = *this * x;}
matrix operator ^ (ll x){
matrix ret;
for (int i=0;i<maxsize;i++)ret[i][i]=1;
matrix tmp = *this;
for (;x;x>>=1,tmp*=tmp){if(x&1)ret *=tmp;}
return ret;
}
void print(){
for (int i=0;i<maxsize;i++){
for (int j=0;j<maxsize;j++)
printf("%d ",a[i][j]);
puts("");
}
}
};
int main(){
scanf("%d%d%d%d",&n,&p,&k,&r);
maxsize=k;
P=p;
matrix base;
for(int i=0;i<k;i++){
base[i][i]++;
base[(i-1+k)%k][i]++;
}
base = base ^ ((ll)n*k);
printf("%d\n",base[0][r]);
return 0;
}
Java
大数
—:在java中的基本头文件(java中叫包)
import java.io.*
importjava.util.* 我们所用的输入 scanner 在这个包中
importjava.math.* 我们下面要用到的 BigInteger 就这这个包中
二:输入与输出
读入 Scanner cin=new Scanner(System.in)
While(cin.hasNext()) //相当于C语言中的!=EOF
n = cin.nextInt(); //输入一个int型整数
n = cin.nextBigInteger(); //输入一个大整数
System.out.print(n); //输出n但不换行
System.out.println(); //换行
System.out.println(n); //输出n并换行
System.out.printf(“%d\n”,n); //类似C语言中的输出
三:定义变量
定义单个变量:
int a,b,c; //和C++ 中无区别
BigInteger a; //定义大数变量a
BigIntegerb= new BigInteger("2"); //定义大数变量 b赋值为 2;
BigDecimaln; //定义大浮点数类 n;
定于数组:
int a[]= new int[10] //定义长度为10的数组a
BigInteger b[] =new BigInteger[100] //定义长度为100的数组a
四:表示范围
布尔型 boolean 1 true,false false
字节型 byte 8 -128-127 0
短整型 short 16 -32768-32767 0
整型 int 32 -2147483648,2147483647 0
长整型 long 64 -9.22E18,9.22E18 0
浮点型 float 32 1.4E-45-3.4028E+38 0.0
双精度型 double 64 4.9E-324,1.7977E+308 0.0
BigInteger任意大的数,原则上只要你的计算机内存足够大,可以有无限位
五:常用的一些操作
A=BigInteger.ONE; //把0赋给A
B=BigInteger. valueOf (3); //把3赋给B;
A[i]=BigInteger. valueOf (10); //把10赋给A[i]
c=a.add(b) //把a与b相加并赋给c
c=a.subtract(b) //把a与b相减并赋给c
c=a.multiply(b) //把a与b相乘并赋给c
c=a.divide(b) //把a与b相除并赋给c
c=a.mod(b) // 相当于a%b
a.pow(b) //相当于$a^b$
a.compareTo(b): //根据该数值是小于、等于、或大于a 返回 -1、0 或 1;
a.equals(b): //判断两数是否相等,也可以用compareTo来代替;
a.min(b),a.max(b): //取两个数的较小、大者;
import java.math.*;
import java.util.*;
public class Main{
public static void main(String [ ] arguments){
Scanner cin = new Scanner(System.in);
int T;
T = cin.nextInt();
BigInteger a,b,c;
BigInteger ans;
while(T>0){
T--;
a=cin.nextBigInteger();
b=cin.nextBigInteger();
c=cin.nextBigInteger();
ans = a.multiply(b);
ans = ans.mod(c);
System.out.println(ans);
}
}
}
读入挂
class InputReader {
private InputStream stream;
private byte[] buf = new byte[1024];
private int curChar;
private int numChars;
private SpaceCharFilter filter;
public InputReader(InputStream stream) {
this.stream = stream;
}
public int read() {
if (numChars == -1)
throw new InputMismatchException();
if (curChar >= numChars) {
curChar = 0;
try {
numChars = stream.read(buf);
} catch (IOException e) {
throw new InputMismatchException();
}
if (numChars <= 0)
return -1;
}
return buf[curChar++];
}
public int readInt() {
int c = read();
while (isSpaceChar(c))
c = read();
int sgn = 1;
if (c == '-') {
sgn = -1;
c = read();
}
int res = 0;
do {
if (c < '0' || c > '9')
throw new InputMismatchException();
res *= 10;
res += c - '0';
c = read();
} while (!isSpaceChar(c));
return res * sgn;
}
public boolean isSpaceChar(int c) {
if (filter != null)
return filter.isSpaceChar(c);
return isWhitespace(c);
}
public static boolean isWhitespace(int c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
}
public interface SpaceCharFilter {
public boolean isSpaceChar(int ch);
}
}
class OutputWriter {
private final PrintWriter writer;
public OutputWriter(OutputStream outputStream) {
writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
}
public void close() {
writer.close();
}
public void printLine(long i) {
writer.println(i);
}
}
STL
string
string str;
stringstream ss;
while(getline(cin,str)){ //getline函数的返回值是其中的流cin。一旦cin读取错误就是false。
ss<<str; //将string送入流中。
int a,sum=0;
while(ss >> a) sum+=a; //当流里没有东西的时候,退出循环。
cout<<sum<<endl;
}
bitset
#include <bitset> //头文件
bitset<n> b; //b 有n 位,每位都为0
bitset<n> b(u); //b 是unsigned long 型u 的一个副本
bitset<n> b(s); //b 是string 对象s 中含有的位串的副本
bitset<n> b(s, pos, n); //b 是s 中从位置pos 开始的n 个位的副本
b.any(); //是否存在1
b.none(); //是否全为0
b.count(); //二进制为1 的个数
b.size(); //bitset 长度
b.flip(); //所有位取反
b.flip(pos); //pos 位取反
b[pos]; //获取pos 位的数
b.set(pos); //pos 位设为1
b.reset(); //全部初始化为0
b.reset(pos); //pos 位初始化0
b.test(pos); //pos 位是否为1
string st=b.to_string();//返回成字符串
deque
queue
priority_queue
priority_queue<int>Q;//采用默认优先级构造队列
priority_queue<int,vector<int>,cmp1>que1;//最小值优先
priority_queue<int,vector<int>,cmp2>que2;//最大值优先
Q.push(x);
int x = Q.top(); Q.pop();
set
multiset
begin() 返回指向第一个元素的迭代器
clear() 清除所有元素
count() 返回某个值元素的个数
empty() 如果集合为空,返回 true
end() 返回指向最后一个元素的迭代器
erase() 删除集合中的元素 ( 参数是一个元素值,或者迭代器)
find() 返回一个指向被查找到元素的迭代器
insert() 在集合中插入元素
size() 集合中元素的数目
lower_bound() 返回指向大于(或等于)某值的第一个元素的迭代器
upper_bound() 返回大于某个值元素的迭代器
equal_range() 返回集合中与给定值相等的上下限的两个迭代器
multiset <point> po;
multiset <point>::iterator L, R, it;
map
map<int,int>::iterator se = mp.upper_bound(mid);
multimap
unordered_map
vector
// erase the 6th element
myvector.erase (myvector.begin()+5);
// erase the first 3 elements:
myvector.erase (myvector.begin(),myvector.begin()+3);
// ------------inserting into a vector-----------
#include <iostream>
#include <vector>
int main (){
std::vector<int> myvector (3,100);
std::vector<int>::iterator it;
it = myvector.begin();
it = myvector.insert ( it , 200 );
myvector.insert (it,2,300);
// "it" no longer valid, get a new one:
it = myvector.begin();
std::vector<int> anothervector (2,400);
myvector.insert (it+2,anothervector.begin(),anothervector.end());
int myarray [] = { 501,502,503 };
myvector.insert (myvector.begin(), myarray, myarray+3);
std::cout << "myvector contains:";
for (it=myvector.begin(); it<myvector.end(); it++)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
stack
stl补充
$lower_bound$: 返回一个ForwardIterator,指向在有序序列范围内的可以插入指定值而不破坏容器顺序的第一个位置。重载函数使用自定义比较操作。
$upper_bound$: 返回一个ForwardIterator,指向在有序序列范围内插入value而不破坏容器顺序的最后一个位置,该位置标志一个大于value的值。重载函数使用自定义比较操作。
$inplace_merge$: 合并两个有序序列,结果序列覆盖两端范围。重载版本使用输入的操作进行排序。
$merge$: 合并两个有序序列,存放到另一个序列。重载版本使用自定义的比较。
$nth_element$: 将范围内的序列重新排序,使所有小于第n个元素的元素都出现在它前面,而大于它的都出现在后面。重载版本使用自定义的比较操作。
$sort$: 以升序重新排列指定范围内的元素。重载版本使用自定义的比较操作。
$stable_sort$: 与sort类似,不过保留相等元素之间的顺序关系。
$unique$: 清除序列中重复元素,和remove类似,它也不能真正删除元素。重载版本使用自定义比较操作。
$next_permutation$: 取出当前范围内的排列,并重新排序为下一个字典序排列。重载版本使用自定义的比较操作。
$prev_permutation$: 取出指定范围内的序列并将它重新排序为上一个字典序排列。如果不存在上一个序列则返回false。重载版本使用自定义的比较操作。
黑科技
输入输出挂
一般输入输出挂。
template<typename T> inline void read(T &x){
x=0;T f=1;char ch;do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');do x=x*10+ch-'0',ch=getchar();while(ch<='9'&&ch>='0');x*=f;
}
template<typename A,typename B> inline void read(A&x,B&y){read(x);read(y);}
template<typename A,typename B,typename C> inline void read(A&x,B&y,C&z){read(x);read(y);read(z);}
template<typename A,typename B,typename C,typename D> inline void read(A&x,B&y,C&z,D&w){read(x);read(y);read(z);read(w);}
加强版。
const int BUFSIZE= 100000000;
char Buf[BUFSIZE+1],*buf=Buf;
template<class T>
void scan(T&a){
for(a=0;*buf<'0'||*buf>'9';buf++);
while(*buf>='0'&&*buf<='9'){a=a*10+(*buf-'0');buf++;}}
int main() {
fread(Buf,1,BUFSIZE,stdin);
}
超神输出输出挂。
#include <bits/stdc++.h>
using namespace std;
namespace IO {
const int MT = 10 * 1024 * 1024; /// 10MB 请注意输入数据的大小!!!
char IO_BUF[MT];
int IO_PTR, IO_SZ;
/// 要记得把这一行添加到main 函数第一行!!!
void begin() {
IO_PTR = 0;
IO_SZ = fread (IO_BUF, 1, MT, stdin);
}
template<typename T>
inline bool scan_d (T & t) {
while (IO_PTR < IO_SZ && IO_BUF[IO_PTR] != '-' && (IO_BUF[IO_PTR] < '0' || IO_BUF[IO_PTR] > '9'))
IO_PTR ++;
if (IO_PTR >= IO_SZ) return false;
bool sgn = false;
if (IO_BUF[IO_PTR] == '-') sgn = true, IO_PTR ++;
for (t = 0; IO_PTR < IO_SZ && '0' <= IO_BUF[IO_PTR] && IO_BUF[IO_PTR] <= '9'; IO_PTR ++)
t = t * 10 + IO_BUF[IO_PTR] - '0';
if (sgn) t = -t;
return true;
}
inline bool scan_s (char s[]) {
while (IO_PTR < IO_SZ && (IO_BUF[IO_PTR] == ' ' || IO_BUF[IO_PTR] == '\n') ) IO_PTR ++;
if (IO_PTR >= IO_SZ) return false;
int len = 0;
while (IO_PTR < IO_SZ && IO_BUF[IO_PTR] != ' ' && IO_BUF[IO_PTR] != '\n')
s[len ++] = IO_BUF[IO_PTR], IO_PTR ++;
s[len] = '\0';
return true;
}
template<typename T>
void print(T x) {
static char s[33], *s1; s1 = s;
if (!x) *s1++ = '0';
if (x < 0) putchar('-'), x = -x;
while(x) *s1++ = (x % 10 + '0'), x /= 10;
while(s1-- != s) putchar(*s1);
}
template<typename T>
void println(T x) {
print(x); putchar('\n');
}
};
using namespace IO;
int main(){
begin();
return 0;
}
离散化
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(a+1,a+1+n);
int cnt = unique(a+1,a+1+n)-(a+1);
for(int i=1;i<=n;i++)
b[i] = lower_bound(a+1,a+1+n,b[i])-a;
构造
N皇后
以下是找到的N皇后一组解得构造法:
一、当n mod 6 != 2 或 n mod 6 != 3时,有一个解为:
2,4,6,8,...,n,1,3,5,7,...,n-1 (n为偶数)
2,4,6,8,...,n-1,1,3,5,7,...,n (n为奇数)
(上面序列第i个数为ai,表示在第i行ai列放一个皇后;... 省略的序列中,相邻两数以2递增。下同)
二、当n mod 6 == 2 或 n mod 6 == 3时,
(当n为偶数,k=n/2;当n为奇数,k=(n-1)/2)
k,k+2,k+4,...,n,2,4,...,k-2,k+3,k+5,...,n-1,1,3,5,...,k+1 (k为偶数,n为偶数)
k,k+2,k+4,...,n-1,2,4,...,k-2,k+3,k+5,...,n-2,1,3,5,...,k+1,n (k为偶数,n为奇数)
k,k+2,k+4,...,n-1,1,3,5,...,k-2,k+3,...,n,2,4,...,k+1 (k为奇数,n为偶数)
k,k+2,k+4,...,n-2,1,3,5,...,k-2,k+3,...,n-1,2,4,...,k+1,n (k为奇数,n为奇数)
数学公式
级数
1.$\sum_{i=1}{n}i2=\frac{n(n+1)(2n+1)}{6}$.
2.$\sum_{i=1}{n}i3=\frac{n2(n+1)2}{4}$.
几何级数
1.$\sum_{i=0}{n}ci=\frac{c^{n+1}-1}{c-1}$,$c \neq 1$.
2.$\sum_{i=0}{\infty}ci=\frac{1}{1-c}$,$|c|<1$.
3.$\sum_{i=1}{\infty}ci=\frac{c}{1-c}$,$|c|<1$.
4.$\sum_{i=0}{n}ici=\frac{nc{n+2}-(n+1)c+c}{(c-1)^2}$,$c \neq 1$.
5.$\sum_{i=0}{\infty}ici=\frac{c}{(1-c)^2}$,$|c|<1$.
调和级数
积分表
1.$\int \frac{1}{x} dx= \ln x$.
2.$\int u\frac{du}{dx}dx = uv - \int v \frac{du}{dx}dx$.
3.$\int \tan x dx = -\ln |\cos x|$.
4.$\int \cot x dx = \ln |\cos x|$.
5.$\int \sec x dx = \ln |\sec x + \tan x|$.
6.$\int \csc x dx = \ln |\csc x + \cot x|$.
泰勒展开
1.$\frac{1}{1-x}=1+x+x2+x3+...$.
2.$ex=\sum_{i=0}\frac{x^i}{i!}$.
3.$\sin x = \sum_{i=0}^{\infty }(-1)^i \frac{x^{2i+1}}{(2i+1)!}$.
4.$\cos x = \sum_{i=0}^{\infty }(-1)^i \frac{x^{2i}}{(2i)!}$.
斐波那契
定义:$F_i=F_{i-1}+F_{i-2}$,$F_0=F_1=1$.
1.$F_{i+1}F_{i-1}-{F_i}2=(-1)i$.
2.$F_1+F_2+...+F_n=F_{n+2}-1$.
3.$gcd(F_n,F_m)=F_{gcd(n,m)}$.