目录
快速幂
注意:在最后输出ans时要再模一次p,以免被指数为0模数为1的情况卡掉
namespace QUICKPOWER{
ll quick_power(ll x,ll y,ll md) {
ll s=1;
while(y) {
if(y&1) s=s*x%md;
y>>=1,x=x*x%md;
}
return s%md;
}
}
逆元、组合数
namespace COMBINATION{
ll quick_power(ll x,ll y,ll P) {
ll s=1;
while(y) {
if(y&1) s=s*x%P;
y>>=1,x=x*x%P;
}
return s%P;
}
ll inv(ll x,ll P) {
return quick_power(x,P-2,P);
}
ll C(ll x,ll y,ll P) {
ll s=1;
for(int i=1,j=x;i<=y;i++,j--) {
s=s*j%P*inv(i,P)%P;
}
return s;
}
}
矩阵快速幂
#include<bits/stdc++.h>
using namespace std;
#define maxn 100
#define md (int)(1e9+7)
#define ll long long
int n;
ll K;
struct Node{
ll a[maxn+5][maxn+5];
Node(){memset(a,0,sizeof(a));}
void build() {for(int i=1;i<=n;i++) a[i][i]=1;}
};
Node operator * (const Node& x,const Node& y) {
Node z;
for(int k=1;k<=n;k++) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%md)%md;
}
}
}
return z;
}
Node a,ans;
int main() {
scanf("%d%lld",&n,&K);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%lld",&a.a[i][j]);
ans.build();
do{
if(K&1) ans=ans*a;
a=a*a,K>>=1;
} while(K);
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) printf("%lld ",ans.a[i][j]);
printf("\n");
}
return 0;
}
矩阵优化
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define md ((int)1e9+7)
struct mtx{
ll a[5][5];
mtx operator * (const mtx e) const {
mtx s;
memset(s.a,0,sizeof(s.a));
for(int i=1;i<=3;i++) {
for(int j=1;j<=3;j++) {
for(int k=1;k<=3;k++) {
s.a[i][j]=(s.a[i][j]+a[i][k]*e.a[k][j])%md;
}
}
}
return s;
}
void print() {
for(int i=1;i<=3;i++) {
for(int j=1;j<=3;j++) {
printf("%lld ",a[i][j]);
}
printf("\n");
}
}
};
int main() {
int T;
scanf("%d",&T);
while(T--) {
int n;
scanf("%d",&n);
if(n<=3) {
printf("1\n");
continue;
}
mtx ans,x;
x.a[1][1]=1,x.a[1][2]=0,x.a[1][3]=1;
x.a[2][1]=1,x.a[2][2]=0,x.a[2][3]=0;
x.a[3][1]=0,x.a[3][2]=1,x.a[3][3]=0;
memset(ans.a,0,sizeof(ans.a));
for(int i=1;i<=3;i++) ans.a[i][i]=1;
while(n) {
if(n&1) ans=ans*x;
x=x*x,n>>=1;
}
printf("%lld\n",ans.a[2][1]);
}
return 0;
}
线性筛
namespace MAKEPHI{
const int N=1e7;
int n,m;
int Phi[N+5];
vector<int> prm;
void makephi(int _n) {
n=_n;
Phi[1]=1;
for(int i=2;i<=n;i++) {
Phi[i]=i;
}
for(int i=2;i<=n;i++) {
if(Phi[i]==i) Phi[i]--,prm.push_back(i);
for(int j=0;j<prm.size()&&i*prm[j]<=n;j++) {
int x=prm[j];
if(i%x) Phi[i*x]=Phi[i]*x;
else Phi[i*x]=Phi[i]*Phi[x];
}
}
}
}
数位dp
windy数的实现 (不含前导零且相邻两个数字之差至少为 2 的正整数) :
#include<bits/stdc++.h>
using namespace std;
#define read(x) scanf("%d",&x)
int L,R;
int a[20];
int f[20][20][2];
int dp(int x,int fa,int lmt) {
if(x==0) return 1;
if(fa!=-1&&f[x][fa][lmt]!=-1) return f[x][fa][lmt];
int up=9;
if(lmt) up=a[x];
int ans=0;
for(int i=0;i<=up;i++) {
if(fa!=-1&&abs(i-fa)<2) continue;
ans+=dp(x-1,(i==0&&fa==-1)?-1:i,lmt&&i==up);
}
if(fa!=-1) f[x][fa][lmt]=ans;
return ans;
}
int slv(int x) {
memset(f,-1,sizeof(f));
int len=0;
while(x) {
a[++len]=x%10;
x/=10;
}
return dp(len,-1,1);
}
int main() {
read(L),read(R);
printf("%d",slv(R)-slv(L-1));
return 0;
}
kmp
注意:nxt数组的意义——最长公共前后缀长
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000000
char a[maxn+5],b[maxn+5];
int nxt[maxn+5];
int n,m;
int main(){
scanf("%s%s",a+1,b+1);
n=strlen(a+1),m=strlen(b+1);
for(int i=2;i<=m;i++) {
int j=nxt[i-1];
while(j&&b[i]!=b[j+1]) j=nxt[j];
if(b[j+1]!=b[i]) nxt[i]=0;
else nxt[i]=j+1;
}
int j=0;
for(int i=1;i<=n;i++) {
while(a[i]!=b[j+1]&&j) j=nxt[j];
if(a[i]==b[j+1]) j++;
if(j==m) {
printf("%d\n",i-m+1);
}
}
for(int i=1;i<=m;i++) printf("%d ",nxt[i]);
return 0;
}
AC自动机
#include<bits/stdc++.h>
using namespace std;
#define maxn ((int)1e6)
#define S 26
struct ACautomation{
int ch[maxn+5][S+5],sz; //trie数
int fail[maxn+5]; //fail指针
int val[maxn+5]; //以当前节点结尾的模式串有几个
ACautomation() { //初始化
memset(ch,0,sizeof(ch)),sz=0;
memset(fail,0,sizeof(fail));
}
void insert(string x) { //加入一个模式串
int u=0;
for(int i=0;i<x.size();i++) {
int y=x[i]-'a';
if(!ch[u][y]) ch[u][y]=++sz; //添加一个新节点
u=ch[u][y];
}
++val[u];
}
void make_fail() { //建立fail指针
queue<int> que;
for(int i=0;i<S;i++) {
if(ch[0][i]) que.push(ch[0][i]);
}
while(!que.empty()) {
int u=que.front();que.pop();
for(int i=0;i<S;i++) {
if(ch[u][i]) {
que.push(ch[u][i]);
fail[ch[u][i]]=ch[fail[u]][i];
} else ch[u][i]=ch[fail[u]][i];
}
}
}
int match(string x) { //匹配
int u=0,sum=0;
for(int i=0;i<x.size();i++) {
int y=x[i]-'a';
u=ch[u][y];
for(int j=u;j&&(~val[j]);j=fail[j]) {
sum+=val[j];
val[j]=-1;
}
}
return sum;
}
};
ACautomation ac;
int main() {
int n;
cin>>n;
for(int i=1;i<=n;i++) {
string x;
cin>>x;
ac.insert(x);
}
ac.make_fail();
string s;
cin>>s;
cout<<ac.match(s);
return 0;
}
字符串哈希
注意:
1、可以用 unsigned long long (ull)的自然溢出处理取模问题
2、使用双哈希不容易产生冲突
#include<bits/stdc++.h>
using namespace std;
#define maxn 10000
#define maxm 1500
#define ull unsigned long long
#define read(x) scanf("%d",&x)
struct Pair{
ull x,y;
Pair(){}
Pair(ull xx,ull yy) {x=xx,y=yy;}
bool operator < (const Pair& oth) const {
return x<oth.x||(x==oth.x&&y<oth.y);
}
bool operator == (const Pair& oth) const {
return x==oth.x&&y==oth.y;
}
};
int n;
Pair hsh[maxn+5];
const int md1=13,md2=131;
void make_hash(int x,char* s,int m) {
ull hsh1=0,hsh2=0;
for(int i=0;i<m;i++) {
hsh1=hsh1*md1+s[i]+1;
hsh2=hsh2*md2+s[i]+1;
}
hsh[x]=Pair(hsh1,hsh2);
}
int main() {
read(n);
for(int i=1;i<=n;i++) {
char s[maxm+5];
scanf("%s",s);
make_hash(i,s,strlen(s));
}
sort(hsh+1,hsh+n+1);
int ans=0;
for(int i=1;i<=n;i++) {
if(hsh[i]==hsh[i-1]) continue;
ans++;
}
printf("%d",ans);
return 0;
}
最小生成树
注意:
1、正向定义小于运算符
2、图不连通的判断方法是求出的最小生成树的边数不等于n-1
3、边集的数组大小要开maxm,开成了maxn就彻底凉了
4、接上,n==m时一定要注意,不要把n、m弄反
#include<bits/stdc++.h>
using namespace std;
#define maxn 5000
#define maxm 200000
#define read(x) scanf("%d",&x)
struct Edge{
int x,y,z;
Edge(){}
Edge(int xx,int yy,int zz) {
x=xx,y=yy,z=zz;
}
bool operator < (const Edge& oth) const {
return z<oth.z;
}
};
int n,m;
Edge e[maxm+5];
int fa[maxn+5];
int find(int x) {
if(fa[x]) return fa[x]=find(fa[x]);
else return x;
}
int kruskal() {
int cnt=0,s=0;
for(int i=1;i<=m;i++) {
int fa1=find(e[i].x),fa2=find(e[i].y);
if(fa1==fa2) continue;
fa[fa1]=fa2;
cnt++;
s+=e[i].z;
}
if(cnt==n-1) return s;
else return -1;
}
int main() {
read(n),read(m);
for(int i=1;i<=m;i++) read(e[i].x),read(e[i].y),read(e[i].z);
sort(e+1,e+m+1);
int ans=kruskal();
if(ans==-1) printf("orz");
else printf("%d",ans);
return 0;
}
最短路:dijkstra
namespace DIJKSTRA{
typedef long long ll;
typedef pair<int,ll> pil;
const int N=2e6;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n;
vector<pil> g[N+5];
ll dist[N+5];
bool vis[N+5];
pil fa[N+5]; //记录路径
struct cmp{
bool operator () (pil e1,pil e2) {
return e1.second>e2.second;
}
};
priority_queue<pil,vector<pil>,cmp> que;
void init(int _n,int st) {
n=_n;
for(int i=1;i<=n;i++) g[i].clear(),vis[i]=0,dist[i]=inf;
dist[st]=0;
}
void addedge(int u,int v,ll w) {
g[u].push_back({v,w});
}
ll dijkstra(int st,int ed) {
que.push({st,0});
while(!que.empty()) {
int x=que.top().first;
que.pop();
if(vis[x]) continue;
else vis[x]=true;
for(pil t:g[x]) {
int y=t.first;
ll w=t.second;
if(dist[y]>dist[x]+w) {
dist[y]=dist[x]+w;
que.push({y,dist[y]});
fa[y]={x,w};
}
}
}
return dist[ed];
}
}
最短路:floyd
#include<bits/stdc++.h>
using namespace std;
#define maxn 100
#define maxm 10000
#define read(x) scanf("%d",&x)
#define inf (1<<30)
int n,m;
int a[maxm+5];
int g[maxn+5][maxn+5];
int dist[maxn+5][maxn+5];
void readin() {
read(n),read(m);
for(int i=1;i<=m;i++) read(a[i]);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) read(g[i][j]);
}
void floyd() {
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dist[i][j]=g[i][j];
for(int k=1;k<=n;k++) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
}
}
}
}
void print() {
int ans=0;
for(int i=2;i<=m;i++) {
ans+=dist[a[i-1]][a[i]];
}
printf("%d",ans);
}
int main() {
readin();
floyd();
print();
return 0;
}
lca倍增
注意:处理到第2^20个祖先就足够过至少1e6的数据范围了,开大了会影响效率
#include<bits/stdc++.h>
using namespace std;
#define maxn 500000
#define maxm 500000
#define read(x) scanf("%d",&x)
int n,m,rt;
vector<int> tr[maxn+5];
int anc[maxn+5][30];
int d[maxn+5];
void dfs(int x,int fa) {
anc[x][0]=fa;
for(int i=1;i<=20;i++) {
anc[x][i]=anc[anc[x][i-1]][i-1];
}
d[x]=d[fa]+1;
for(int i=0;i<tr[x].size();i++) {
int y=tr[x][i];
if(y==fa) continue;
dfs(y,x);
}
}
int findLCA(int x,int y) {
if(d[x]<d[y]) swap(x,y);
for(int i=20;i>=0;i--) {
if(d[anc[x][i]]>=d[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 anc[x][0];
}
int main() {
read(n),read(m),read(rt);
for(int i=1;i<n;i++) {
int x,y;
read(x),read(y);
tr[x].push_back(y);
tr[y].push_back(x);
}
dfs(rt,0);
while(m--) {
int x,y;
read(x),read(y);
printf("%d\n",findLCA(x,y));
}
return 0;
}
割点
注意:第一个节点的特判
#include<bits/stdc++.h>
using namespace std;
#define read(x) scanf("%d",&x)
#define maxn 100000
int n,m;
vector<int> g[maxn+5];
int pre[maxn+5],low[maxn+5],cnt;
bool iscut[maxn+5];
void tarjan(int x,int fa) {
pre[x]=low[x]=++cnt;
int chd=0;
for(int i=0;i<g[x].size();i++) {
int y=g[x][i];
if(y==fa) continue;
if(!pre[y]) {
chd++;
tarjan(y,x);
if(pre[x]<=low[y]&&fa) iscut[x]=true;
low[x]=min(low[x],low[y]);
} else if(pre[y]<pre[x]) low[x]=min(low[x],pre[y]);
}
if(fa==0&&chd>1) iscut[x]=true;
}
int main() {
read(n),read(m);
for(int i=1;i<=m;i++) {
int x,y;
read(x),read(y);
g[x].push_back(y),g[y].push_back(x);
}
for(int i=1;i<=n;i++) {
if(!pre[i]) tarjan(i,0);
}
int ans=0;
for(int i=1;i<=n;i++) if(iscut[i]) ans++;
printf("%d\n",ans);
for(int i=1;i<=n;i++) if(iscut[i]) printf("%d ",i);
return 0;
}
缩点
注意:找反边时注意要在同一次dfs中遍历到的点才能算,即需要判断col[y]==0
#include<bits/stdc++.h>
using namespace std;
#define read(x) scanf("%d",&x)
#define maxn 100000
int n,m;
vector<int> g[maxn+5];
int v[maxn+5];
int pre[maxn+5],low[maxn+5],cnt=0;
stack<int> stk;
int col[maxn+5],sum,w[maxn+5];
vector<int> a[maxn+5];
int dist[maxn+5];
int dfs(int x) {
if(dist[x]) return dist[x];
for(int i=0;i<a[x].size();i++) {
dist[x]=max(dist[x],dfs(a[x][i]));
}
dist[x]+=w[x];
return dist[x];
}
void make_a() {
for(int i=1;i<=n;i++) {
for(int j=0;j<g[i].size();j++) {
int y=g[i][j];
if(col[i]!=col[y]) a[col[i]].push_back(col[y]);
}
}
}
void tarjan(int x) {
pre[x]=low[x]=++cnt;
stk.push(x);
for(int i=0;i<g[x].size();i++) {
int y=g[x][i];
if(!pre[y]) {
tarjan(y);
low[x]=min(low[x],low[y]);
} else if(pre[x]>pre[y]&&!col[y]) low[x]=min(low[x],pre[y]);
}
if(low[x]>=pre[x]) {
int u;sum++;
do{
u=stk.top();stk.pop();
col[u]=sum;
w[sum]+=v[u];
} while(u!=x);
}
}
int main() {
read(n),read(m);
for(int i=1;i<=n;i++) read(v[i]);
for(int i=1;i<=m;i++) {
int x,y;
read(x),read(y);
g[x].push_back(y);
}
for(int i=1;i<=n;i++) {
if(!pre[i]) tarjan(i);
}
make_a();
for(int i=1;i<=sum;i++) if(!dist[i]) dfs(i);
int ans=0;
for(int i=1;i<=sum;i++) ans=max(ans,dist[i]);
printf("%d",ans);
return 0;
}
二分图匹配
#include<bits/stdc++.h>
using namespace std;
#define read(x) scanf("%d",&x)
#define maxn 500
#define maxm ((int)5e4)
int n1,n2,m;
vector<int> a[maxn*2+5];
int match[maxn*2+5],use[maxn*2+5];
bool dfs(int x) {
for(int i=0; i<a[x].size(); i++) {
int y=a[x][i];
if(use[y]) continue;
use[y]=true;
if(!match[y]||dfs(match[y])) {
match[x]=y,match[y]=x;
return true;
}
}
return false;
}
int main() {
read(n1),read(n2),read(m);
for(int i=1; i<=m; i++) {
int x,y;
read(x),read(y);
y+=maxn;
a[x].push_back(y),a[y].push_back(x);
}
int s=0;
for(int i=1; i<=n1; i++) {
memset(use,0,sizeof(use));
if(!match[i]) dfs(i);
}
for(int i=1; i<=n1; i++) if(match[i]) s++;
printf("%d",s);
return 0;
}
最大流·dinic
#include<bits/stdc++.h>
using namespace std;
#define maxn 200
#define maxm 5000
#define inf 2147483647
#define ll long long
#define read(x) scanf("%d",&x)
struct Edge {
int x,y,z;
Edge() {}
Edge(int _x,int _y,int _z) {
x=_x,y=_y,z=_z;
}
};
int n,m,S,T;
int h[maxn+5],nxt[maxm*2+5],cnt=-1; //存图
Edge e[maxm*2+5]; //0~cnt
int cur[maxn+5]; //当前弧优化
int d[maxn+5]; //分层
queue<int> que;
void add_edge(int u,int v,int w) {
e[++cnt]=Edge(u,v,w);
nxt[cnt]=h[u];
h[u]=cnt;
}
int bfs() {
memset(d,0,sizeof(d));
d[S]=1;
que.push(S);
while(!que.empty()) {
int x=que.front();
que.pop();
for(int i=h[x]; ~i; i=nxt[i]) {
int y=e[i].y,z=e[i].z;
if(d[y]||z==0) continue;
d[y]=d[x]+1;
que.push(y);
}
}
return d[T];
}
int dfs(int x,int w) {
if(x==T) return w; //找到增广路
for(int& i=cur[x]; ~i; i=nxt[i]) { //&!
int y=e[i].y,z=e[i].z;
if(d[y]!=d[x]+1||z==0) continue;
int Min=dfs(y,min(w,z)); //更新当前增广路上的最小值
if(Min>0) {
e[i].z-=Min,e[i^1].z+=Min;
return Min;
}
}
return 0;
}
ll dinic() {
ll ans=0;
while(bfs()) { //存在增广路
for(int i=1; i<=n; i++) cur[i]=h[i];
while(int x=dfs(S,inf)) ans+=x;
}
return ans;
}
int main() {
memset(nxt,-1,sizeof(nxt));
memset(h,-1,sizeof(h));
read(n),read(m),read(S),read(T);
for(int i=1; i<=m; i++) {
int u,v,w;
read(u),read(v),read(w);
add_edge(u,v,w);
add_edge(v,u,0);
}
ll ans=dinic();
printf("%lld",ans);
return 0;
}
费用流·dinic
注意:dfs的时候需要记录访问过的点,不要再次访问。最大流本来就是分层图,所以不需要。
#include<bits/stdc++.h>
using namespace std;
#define read(x) scanf("%d",&x)
struct Edge{
int x,y,z,f;
Edge() {}
Edge(int _x,int _y,int _z,int _f) {x=_x,y=_y,z=_z,f=_f;}
};
#define maxn ((int)5e3)
#define maxm ((int)5e4)
int n,m,S,T;
Edge e[maxm*2+5];
int h[maxn+5],nxt[maxm*2+5],cnt=-1;
void add_edge(int x,int y,int z,int f) {
e[++cnt]=Edge(x,y,z,f);
nxt[cnt]=h[x];
h[x]=cnt;
}
queue<int> que;
int d[maxn+5];
bool inque[maxn+5];
bool spfa() {
for(int i=1;i<=n;i++) d[i]=(1<<30);
que.push(S);d[S]=0,inque[S]=true;
while(!que.empty()) {
int x=que.front();que.pop();
inque[S]=false;
for(int i=h[x]; ~i; i=nxt[i]) {
int y=e[i].y,z=e[i].z,f=e[i].f;
if(z&&d[x]+f<d[y]) {
d[y]=d[x]+f;
inque[y]=true,que.push(y);
}
}
}
return d[T]!=(1<<30);
}
int cur[maxn+5];
int sumw=0,ans=0;
bool use[maxn+5];
int dfs(int x,int w) {
use[x]=true;
if(x==T) return w;
for(int& i=cur[x]; ~i; i=nxt[i]) {
int y=e[i].y,z=e[i].z,f=e[i].f;
if(use[y]||d[y]!=d[x]+f||z==0) continue;
int Min=dfs(y,min(w,z));
if(Min) {
e[i].z-=Min,e[i^1].z+=Min;
sumw+=(f*Min);
return Min;
}
}
return 0;
}
void dinic() {
while(spfa()) {
memset(use,0,sizeof(use));
for(int i=1;i<=n;i++) cur[i]=h[i];
while(int x=dfs(S,(1<<30))) ans+=x;
}
}
int main() {
memset(h,-1,sizeof(h)),memset(nxt,-1,sizeof(nxt));
read(n),read(m),read(S),read(T);
for(int i=1;i<=m;i++) {
int x,y,z,f;
read(x),read(y),read(z),read(f);
add_edge(x,y,z,f);
add_edge(y,x,0,-f);
}
dinic();
printf("%d %d",ans,sumw);
return 0;
}
树状数组
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
namespace BIT{
typedef long long ll;
const int N=2e6;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n;
ll c[N+5];
void init(int _n) {
n=_n;
for(int i=1;i<=n;i++) c[i]=0;
}
int lowbit(int x) {
return x&-x;
}
void modify(int x,int d) { //单点修改
for(int i=x;i<=n;i+=lowbit(i)) {
c[i]+=d;
}
}
ll query(int x) { //单点询问
ll s=0;
for(int i=x;i>0;i-=lowbit(i)) {
s=s+c[i];
}
return s;
}
}
线段树
namespace SEGMENT_TREE{
// const int N=3e5;
ll c[N+5];
ll a[N*4+5],lzy[N*4+5];
#define lson (o*2)
#define rson (o*2+1)
#define mid ((l+r)>>1)
void push_down(int o,int l,int r) {
a[lson]+=(mid-l+1)*lzy[o],a[rson]+=(r-mid)*lzy[o];
lzy[lson]+=lzy[o],lzy[rson]+=lzy[o];
lzy[o]=0;
return ;
}
void push_up(int o) {
a[o]=a[lson]+a[rson];
return ;
}
void build(int o,int l,int r) { //建树
if(l==r) {
a[o]=c[l];
return ;
}
build(lson,l,mid),build(rson,mid+1,r);
push_up(o);
return ;
}
void modify(int o,int l,int r,int L,int R,ll d) { //区间修改
if(l>R||r<L) return ;
if(l>=L&&r<=R) {
a[o]+=d*(r-l+1);
lzy[o]+=d;
return ;
}
push_down(o,l,r);
modify(lson,l,mid,L,R,d),modify(rson,mid+1,r,L,R,d);
push_up(o);
return ;
}
ll query(int o,int l,int r,int L,int R) { //区间查询
if(l>R||r<L) return 0;
if(l>=L&&r<=R) {
return a[o];
}
push_down(o,l,r);
ll s=query(lson,l,mid,L,R)+query(rson,mid+1,r,L,R);
return s;
}
}
莫队
#include<bits/stdc++.h>
using namespace std;
#define maxn 50000
#define ll long long
#define read(x) scanf("%d",&x)
struct Pair{
int x,y;
int Id;
bool insq;
Pair(){}
};
int cc;
bool cmp(const Pair& x,const Pair& y) {
return x.x/cc==y.x/cc?x.y<y.y:x.x<y.x;
}
int n,m;
int a[maxn+5];
Pair q[maxn+5];
int ans[maxn+5];
int cnt[maxn+5];
int pans[maxn+5];
ll pans2[maxn+5];
int gcd(ll x,ll y) {
if(y==0) return x;
return gcd(y,x%y);
}
int main() {
read(n),read(m);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=m;i++) read(q[i].x),read(q[i].y),q[i].Id=i;
cc=sqrt(n);
sort(q+1,q+m+1,cmp);
int L=1,R=0;
for(int i=1;i<=m;i++) {
ans[i]=ans[i-1];
while(L<q[i].x) ans[i]+=1-2*cnt[a[L]],cnt[a[L]]--,L++;
while(L>q[i].x) L--,ans[i]+=2*cnt[a[L]]+1,cnt[a[L]]++;
while(R>q[i].y) ans[i]+=1-2*cnt[a[R]],cnt[a[R]]--,R--;
while(R<q[i].y) R++,ans[i]+=2*cnt[a[R]]+1,cnt[a[R]]++;
if(L==R) {pans[q[i].Id]=0,pans2[q[i].Id]=1;continue;}
pans[q[i].Id]=ans[i]-(q[i].y-q[i].x+1),pans2[q[i].Id]=(q[i].y-q[i].x+1)*((ll)q[i].y-q[i].x);
}
for(int i=1;i<=m;i++) {
ll x=pans[i],y=pans2[i];
ll g=gcd(x,y);
printf("%lld/%lld\n",x/g,y/g);
}
return 0;
}
带修莫队
块的大小 n 2 / 3 n^{2/3} n2/3,复杂度 O ( n 5 / 3 ) O(n^{5/3}) O(n5/3)。
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000000
#define read(x) scanf("%d",&x)
struct Q{
int l,r;
int Id,lstc,insq;
Q(){}
};
struct C{
int x,v;
C(){}
};
int n,m;
int a[maxn+5];
Q q[maxn+5];
C c[maxn+5];
int mq,mc;
int cc;
bool cmp(const Q& x,const Q& y) {
if(x.l/cc!=y.l/cc) return x.l<y.l;
if(x.r/cc!=y.r/cc) return x.r<y.r;
return x.lstc<y.lstc;
}
int cnt[maxn+5],s=0;
int ans[maxn+5];
void Add(int x) {if(!cnt[x]++) s++;}
void Del(int x) {if(!--cnt[x]) s--;}
void Work(int i,int t) {
if(c[t].x>=q[i].l&&c[t].x<=q[i].r) {
if(!--cnt[a[c[t].x]]) s--;
if(!cnt[c[t].v]++) s++;
}
swap(c[t].v,a[c[t].x]);
}
int main() {
read(n),read(m);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=m;i++) {
char opr;
while(scanf("%c",&opr)&&!isalpha(opr));
if(opr=='Q') {
read(q[++mq].l),read(q[mq].r);
q[mq].Id=mq,q[mq].lstc=mc;
}
else {
read(c[++mc].x),read(c[mc].v);
}
}
cc=(int)pow(n,2.0/3);
sort(q+1,q+mq+1,cmp);
int L=1,R=0,T=0;
for(int i=1;i<=mq;i++) {
while(L>q[i].l) Add(a[--L]);
while(L<q[i].l) Del(a[L++]);
while(R>q[i].r) Del(a[R--]);
while(R<q[i].r) Add(a[++R]);
while(T<q[i].lstc) Work(i,++T);
while(T>q[i].lstc) Work(i,T--);
ans[q[i].Id]=s;
}
for(int i=1;i<=mq;i++) printf("%d\n",ans[i]);
return 0;
}