啊还有十天不到就要noip提高组了,还是觉得好慌张,周围一大堆大佬,唔菜鸡还是背背模板吧。
每天一个部分好啦。
第一部分:数论+线段树+树状数组+rmq+最短路+最小生成树(是不是觉得非常的繁杂哈哈哈我就喜欢先上一大堆最主要的)
快速幂
用途:用来计算a^b mod n的值,且复杂度为log级
假设我们要求a^b,那么其实b是可以拆成二进制的,该二进制数第i位的权为2^(i-1),例如当b==11时a^11=a^(2^0+2^1+2^3)
11的二进制是1011,11 = 23×1 + 22×0 + 21×1 + 2o×1,因此,我们将a11转化为算 a^(2^0)*a^(2^1)*a^(2^3) ,由于是二进制,很自然地想到用位运算这个强大的工具:
& 和 >>
&运算通常用于二进制取位操作,例如一个数 & 1 的结果就是取二进制的最末位。还可以判断奇偶x&1==0为偶,x&1==1为奇。 >>运算比较单纯,二进制去掉最后一位。
function pow(a,b:longint):longint; var ans,base:longint; begin ans:=1; base:=a; while b<>0 do begin if b and 1<>0 then ans:=ans*base; base:=base*base; b:=b>>1; end; exit(ans); end;
矩阵乘法
//p是第一个矩阵的行数 //q是第二个矩阵的行数 //r是第二个矩阵的列数 for i:=1 to p do for j:=1 to r do for k:=1 to q do c[i,j]:=c[i,j]+a[i,k]*b[k,j];
扩展欧几里得
若存在一组解x0,y0,满足b*x0+(a mod b)*y0=d则取x=y0,y=x0-(a div b)*y0,有ax+by=d这样,我们可以用类似辗转相除的迭代法求解。
function exgcd(a,b:longint;var x,y:longint):longint; var d,tmp:longint; begin if b=0 then begin x:=1; y:=0; exit(a); end d:=exgcd(b,a mod b,x,y); tmp:=x; x:=y; y:=tmp-a div b*y; exit(d); end;
逆元
什么叫乘法逆元?
这里,我们称 x 是 a 关于 m 的乘法逆元
这怎么求?可以等价于这样的表达式: a*x + m*y = 1
欧拉函数
欧拉函数用希腊字母φ表示,φ(N)表示N的欧拉函数.
对φ(N)的值,我们可以通俗地理解为小于N且与N互质的数的个数(包含1).
1.对于素数p, φ(p)=p-1,对于对两个素数p,q φ(pq)=pq-1
欧拉函数是积性函数,但不是完全积性函数.
即φ(mn)=φ(n)*φ(m)只在(n,m)=1时成立.
2.对于一个正整数N的素数幂分解N=P1^q1*P2^q2*...*Pn^qn.
φ(N)=N*(1-1/P1)*(1-1/P2)*...*(1-1/Pn).
3.除了N=2,φ(N)都是偶数.
4.设N为正整数,∑φ(d)=N (d|N).
根据性质2,我们可以在O(sqrt(n))的时间内求出一个数的欧拉函数值.
如果我们要求1000000以内所有数的欧拉函数,怎么办.
上面的方法复杂度将高达O(N*sqrt(N)).
//求一个n的小于等于n的互质的数的个数 function euler(n:longint):longint; var res,a,i:longint; begin res:=n; a:=n; for i:=2 to trunc(sqrt(a)) do if a mod i=0 then begin res:=(res div i)*(i-1); while a mod i=0 do a:=a div i; end; if a>1 then res:=(res div a)*(a-1); exit(res); end;
//筛法求所有的欧拉函数 procedure init; var i,j:longint; begin fillchar(p,sizeof(p),0); p[1]:=1; for i:=2 to 100000 do if p[i]=0 then begin j:=i; while (j<=100000) do begin if p[j]=0 then p[j]:=j; p[j]:=p[j] div i*(i-1); j:=j+i; end; end; end;
线段树
//区间求和 var t,x,y,n,k,tap:int64; i:longint; a:array[0..1000005]of int64; tree,mark:array[0..4000005]of int64; procedure build(root,t,w:longint); var mid:longint; begin {}mark[root]:=0; if t=w then tree[root]:=a[t] else begin mid:=(t+w) div 2; build(root*2,t,mid); build(root*2+1,mid+1,w); tree[root]:=tree[root*2]+tree[root*2+1]; end; end; procedure pushdown(root,mid,tt,ww:longint); begin if mark[root]<>0 then begin inc(mark[root*2],mark[root]); inc(mark[root*2+1],mark[root]); inc(tree[root*2],mark[root]*(mid-tt+1)); inc(tree[root*2+1],mark[root]*(ww-mid)); mark[root]:=0; end; end; function find(root,tt,ww,t,w:longint):int64; var mid:longint; begin if (t>ww)or(w<tt) then exit(0); if (t<=tt)and(w>=ww) then exit(tree[root]); mid:=(tt+ww) div 2; {}pushdown(root,mid,tt,ww); exit(find(root*2,tt,mid,t,w)+find(root*2+1,mid+1,ww,t,w)); end; procedure update(root,tt,ww,t,w,x:longint); var mid:longint; begin if (tt>w)or(ww<t)then exit; if (t<=tt)and(ww<=w) then begin inc(mark[root],x); inc(tree[root],x*(ww-tt+1)); exit; end; mid:=(tt+ww) div 2; pushdown(root,mid,tt,ww); update(root*2,tt,mid,t,w,x); update(root*2+1,mid+1,ww,t,w,x); tree[root]:=tree[root<<1]+tree[1+(root<<1)]; end; begin readln(n,k); for i:=1 to n do read(a[i]); build(1,1,n); for i:=1 to k do begin read(t,x,y); if t=1 then begin read(tap); update(1,1,n,x,y,tap); end else writeln(find(1,1,n,x,y)); end; end.
树状数组
//树状数组程序 function lowbit(x:longint):longint; begin exit(x and(-x));end; procedure add(x,y:longint); var i:longint; begin i:=x; while i<=n do begin tree[i]:=tree[i]+y; i:=i+lowbit(i); end; end; function sum(x:longint):longint; var i,ans:longint; begin ans:=0; i:=x; while i>0 do begin ans:=ans+tree[i]; i:=i-lowbit(i); end; exit(ans); end; //标准 begin readln(n,m); for i:=1 to n do begin read(a[i]); add(i,a[i]); end; for i:=1 to m do begin read(c); if c=1 then begin readln(x,k); add(x,k);end else begin read(x,y); writeln(sum(y)-sum(x-1));end; end; end. //差分求和 begin readln(n,m); y:=0; for i:=1 to n do begin read(x); add(i,x-y); y:=x; end; for i:=1 to m do begin read(f); if f=1 then begin read(x,y,z); add(x,z); add(y+1,-z); end else begin read(x); writeln(sum(x)); end; end; end.
RMQ
var n,i,j,m,a,b:longint; f:array[0..100000,0..21]of longint; function min(a,b:longint):longint; begin if a<b then exit(a) else exit(b); end; begin readln(n,m); for i:=1 to n do read(f[i,0]); for j:=1 to trunc(ln(n)/ln(2)) do for i:=1 to n do if (i+1<<(j-1)<=n) then f[i,j]:=min(f[i,j-1],f[i+(1<<(j-1)),j-1]); // F[i,j]表示从i开始到i+2的j次 -1这个区间中的最大值 for i:=1 to m do begin read(a,b); j:=trunc(ln(b-a+1)/ln(2)); write(min(f[a,j],f[b-(1<<j)+1,j]),‘ ‘); end; end.
最短路-dijkstra
//单源最短路径(无堆优化) procedure Dijkstra(s:longint); var v,i,u,j:longint; min:int64; begin fillchar(vis,sizeof(vis),false); dis[s]:=0; for i:=1 to n do begin min:=2147483647; for j:=1 to n do if (vis[j]=false)and(dis[j]<min)then begin min:=dis[j]; u:=j; end; vis[u]:=true; for v:=1 to n do if (vis[v]=false)and(dis[u]+f[u,v]<dis[v]) then dis[v]:=dis[u]+f[u,v]; end; for i:=1 to n do write(dis[i],‘ ‘); end; begin readln(n,m,s); for i:=1 to n do for j:=1 to n do f[i,j]:=2147483647; for i:=1 to n do dis[i]:=2147483647; for i:=1 to m do begin read(a,b,c); f[a,b]:=min(c,f[a,b]); end; Dijkstra(s); end.
{single source shortest path}+堆优化 {假设一个图的最大节点数为1000,所有运算在integer范围内} {程序目标:给定有向图的邻接表,求出节点1到节点n的最短路径长度} const maxn=1000;{最大节点数} var n:integer;{节点个数} deg:array[1..maxn] of integer;{每个节点的度数} list:array[1..maxn,1..maxn,1..2] of integer;{邻接表,第一个元素表示边的中点,第二个元素表示边的长度} count:integer;{堆内元素个数计数器} heap:array[1..maxn] of integer;{heap[i]表示堆内的第i的元素的节点编号} pos:array[1..maxn] of integer;{表示编号为i的元素在堆内的位置} key:array[1..maxn] of integer;{表示节点1到节点i的最短距离} exist:array[1..maxn] of boolean;{表示节点i是否存在于堆中} i,j,now:integer; swap别忘了var!!!!!! procedure heapify(p:integer);{调整堆的过程} var best:integer; begin best:=p; if (p*2<=count) and (key[heap[p*2]]<key[heap[best]]) then best:=p*2; if (p*2+1<=count) and (key[heap[p*2+1]]<key[heap[best]]) then best:=p*2+1; if best<>p then begin swap(pos[heap[p]],pos[heap[best]]); swap(heap[p],heap[best]); heapify(best); end; end; procedure modify(id,new_key:integer);{判断new_key与key[id]大小,并修改key[id]大小} var p:integer; begin if (new_key<key[id]) then begin key[id]:=new_key; p:=pos[id]; while (p>1) and (key[heap[p]]<key[heap[p div 2]]) do begin swap(pos[heap[p]],pos[heap[p div 2]]); swap(heap[p],heap[p div 2]); p:=p div 2; end; end; end; procedure extract(var id,dis:integer);{读取堆中最小元素的节点编号和节点1到该节点的距离} begin id:=heap[1]; dis:=key[id]; dec(count); if (count>0) then begin swap(pos[heap[1]],pos[heap[count+1]]); swap(heap[1],heap[count+1]); heapify(1); end; end; begin readln(n); for i:=1 to n do begin read(deg[i]); for j:=1 to deg[i] do read(list[i,j,1],list[i,j,2]); end; for i:=1 to n do begin exist[i]:=true; pos[i]:=i; heap[i]:=i; key[i]:=maxint; end; count:=n; key[1]:=0; {dijkstra算法} while (count>0) do begin extract(i,now); if now=maxint then break; for j:=1 to deg[i] do if exist[list[i,j,1]] then modify(list[i,j,1],now+list[i,j,2]); end; if key[n]=maxint then writeln(‘Not Connected!‘){节点1和节点n不连通} else writeln(key[n]);{连通} end.
最短路-Floyd(多源求最短路)不上代码了
最短路-SPFA
var tot,x,y,z,i,n,m:longint; head,next,key,value:array[0..40000]of longint; dis,q:array[0..100000]of longint; vis:array[0..100000]of boolean; procedure add(x,y,z:longint); begin tot:=tot+1; next[tot]:=head[x]; head[x]:=tot; key[tot]:=y; value[tot]:=z; end; procedure spfa(s:longint); var i,h,t,u,v:longint; begin fillchar(vis,sizeof(vis),false); for i:=1 to n do dis[i]:=10000000; dis[s]:=0;q[1]:=s; h:=0; t:=1; vis[s]:=true; while(h<t) do begin h:=h+1; u:=q[h]; i:=head[u]; vis[u]:=false; while(i<>0) do begin v:=key[i]; if (dis[v]>dis[u]+value[i]) then begin dis[v]:=dis[u]+value[i]; if (vis[v]=false) then begin t:=t+1; q[t]:=v; vis[v]:=true; end; end; i:=next[i]; end; end; end; begin readln(n,m); while (n<>0) do begin tot:=0; fillchar(head,sizeof(head),0); for i:=1 to m do begin read(x,y,z); add(x,y,z); add(y,x,z); end; spfa(1); writeln(dis[n]); readln(n,m); end; end.
并查集(带权)
//带权并查集 function find(x:longint):longint; var tmp:longint; begin if father[x]=x then exit(x); tmp:=father[x]; father[x]:=find(father[x]); //下面一句可以随意改 value[x]:=value[tmp]+1; exit(father[x]); end; //食物链 var n,m,i,ans:longint; father,size:array[0..50000]of longint; function find(x:longint):longint; var t:longint; begin if father[x]=x then exit(x) else begin t:=father[x]; father[x]:=find(father[x]); //路径压缩 size[x]:=(size[x]+size[t]) mod 3; //根据找规律可得,各种情况两两结合可得size exit(father[x]); end; end; function judge:boolean; var kind,x,y,fx,fy:longint; begin //以下的各种判定可用向量的规律判断得 readln(kind,x,y); if (x>n)or(y>n) then exit(false); if (kind=2)and(x=y) then exit(false); fx:=find(x); fy:=find(y); if fx=fy then begin if (size[y]-size[x]+3) mod 3<>(kind-1) then exit(false) else exit(true); end else begin father[fy]:=fx; size[fy]:=(size[x]-size[y]+kind-1+3) mod 3; exit(true); end; end; begin readln(n,m); for i:=1 to n do begin father[i]:=i; size[i]:=0; end; for i:=1 to m do if judge=false then ans:=ans+1; writeln(ans); end.
最小生成树-Prim
算法思想:普里姆算法构造最小生成树的过程是从一个顶点U={u0}作初态,不断贪心寻找与U中顶点相邻且代价最小的边的另一个顶点,扩充到U集合直至U=V为止。
const max=1000; var map:array[1..MXN,1..MXN] of longint; cost:array[1..MXN] of longint; visit:array[1..MXN] of boolean; i,j,n,m,x,y,v:longint; function prim():longint; var i,j,min,mini,ans:longint; begin ans:=0; for i:=1 to n do begin visit[i]:=false;cost[i]:=maxlongint;end; //visit[i]是i点是否被访问的标志,cost[i]是到i点的最小权边。 for i:=2 to n do if map[1,i]<>0 then cost[i]:=map[1,i];visit[1]:=true; for i:=1 to n-1 do begin min:=maxlongint; for j:=1 to n do if not visit[j] and (cost[j]<min) then begin min:=cost[j];mini:=j;end; visit[mini]:=true;inc(ans,min); for j:=1 to n do if not visit[j] and (map[mini,j]>0) and (map[mini,j]<cost[j]) then cost[j]:=map[mini,j]; //更新圈内圈外存储的最短距离 end; exit(ans); end; begin readln(n,m); for i:=1 to m do begin readln(x,y,v); if (map[x,y]=0) or (map[x,y]>v) then begin map[x,y]:=v;map[y,x]:=v; end; end; writeln(prim()); end.
最小生成树-Kruskal算法
先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。
时间复杂度为为O(e^2), 使用并查集优化后复杂度为 O(eloge),与网中的边数有关,适用于求边稀疏的网的最小生成树。
设图G的度为,G=(V,E),设该图的最小生成树为T=(V,TE),设置边的集合TE的初始状态为空集。将图G中的边按权值从小到大排好序,然后从小的依次开始选取,若选取得边使生成树T不形成回路,则把它并入TE中,保留作为T的一条边;若选取得边使生成树形成回路,则舍弃;如此继续进行,直到使TE中包含n-1条边为止。
const MXN=1000; type rqmap=record s,t,v:longint; end; var map:array[1..MXN*MXN] of rqmap; father:array[1..MXN] of longint; n,m,i,ingraph,ans:longint; procedure qsort(b,e:longint);//排序 var i,j,x:longint; t:rqmap; begin i:=b;j:=e;x:=map[(i+j)>>1].v; while (i<=j) do begin while (map[i].v<x) do inc(i); while (map[j].v>x) do dec(j); if (i<=j) then begin t:=map[i];map[i]:=map[j];map[j]:=t;inc(i);dec(j);end; end; if i<e then qsort(i,e); if j>b then qsort(b,j); end; function find(x:longint):longint; begin if (father[x]=x) then exit(x); father[x]:=find(father[x]);//路径压缩 exit(father[x]); end; procedure union(a,b:longint); //并查集 begin father[find(a)]:=find(father[b]); end; begin readln(n,m); for i:=1 to n do father[i]:=i; for i:=1 to m do readln(map[i].s,map[i].t,map[i].v); qsort(1,m);ans:=0;ingraph:=1;i:=0; while (ingraph<n) do begin inc(i); if find(map[i].s)<>find(map[i].t) then begin inc(ingraph);inc(ans,map[i].v);union(map[i].s,map[i].t); end; end; writeln(ans); end.
拓扑排序
将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。
var map:array[0..101,0..101]of longint; c,d:array[0..101]of longint; flag:array[0..101]of boolean; i,j,k,n,x,y:longint; check:boolean; begin fillchar(map,sizeof(map),false); fillchar(c,sizeof(c),0); fillchar(d,sizeof(d),0); readln(n); while not eof do begin readln(x,y); inc(c[x]); inc(d[y]); map[x,c[x]]:=y; end; fillchar(flag,sizeof(flag),false); for k:=1 to n do begin for i:=1 to n do if (flag[i]=false)and(d[i]=0) then begin write(i,‘ ‘); flag[i]:=true; for j:=1 to c[i] do dec(d[map[i,j]]); break; end; end; end.
高精度·加法
var s1,s2:string; a,b:array[1..100] of longint; la,le,len,i:longint; begin readln(s1); readln(s2); la:=length(s1); for i:= 1 to la do a[i]:=ord(s1[la+1-i])-48; le:=length(s2); for i:=1 to le do b[i]:=ord(s2[le+1-i])-48; len:=la; if le>len then len:=le; for i:=1 to len do begin a[i+1]:=a[i+1]+(a[i]+b[i]) div 10; a[i]:=(a[i]+b[i]) mod 10; end; if a[len+1]>0 then len:=len+1;{判断最高位} for i:=len downto 1 do write(a[i]); end.
高精度·减法
const n=1000; type arr=array[0..n]of longint; var a,b:arr;s:string; function f:boolean; var i:longint=1; begin f:=true; while ((i<n)and(a[i]=b[i])) do inc(i); if a[i]<=b[i] then f:=false; end; procedure init(var p:arr); var m:char; k,i:longint; begin k:=1; read(m); while m in[‘0‘..‘9‘] do begin p[k]:=ord(m)-48;k:=k+1;read(m);{使得能够不断读数} end; for i:=1 to k do begin p[n+i-k]:=p[i];p[i]:=0;{把最小位放在前面,便于顺次详见} end; end; procedure work; var t,g:longint;i:longint; begin if f then write(‘‘); if not f then begin s:=‘-‘; {判断减数和被减数大小,如果被减数大于减数就填负号后交换位置} for i:=1 to n do begin t:=a[i];a[i]:=b[i];b[i]:=t; end; end; g:=0;{进位标志} for i:=n downto 1 do begin if a[i]>=b[i]+g then begin a[i]:=a[i]-b[i]-g;g:=0; end{不需要借位时} else begin a[i]:=10+a[i]-b[i]-g;g:=1; end; end; end; procedure output; var i:longint=1; begin while((a[i]=0)and(i<n))do inc(i); {当在范围限度内且为零时就不断不断向后移以寻找做完减法后的最高位} while i<n do begin write(a[i]);inc(i); end; end; begin fillchar(a,sizeof(a),0);fillchar(b,sizeof(b),0); init(a); readln; init(b);readln; work; write(s);output; end.
高精度·乘法
const max=200; type arr=array[1..max]of longint; arrr=array[1..2*max] of longint; var a,b:arr;c:arrr;k,s1,s2,n:longint; procedure init(var p:arr); var s:string; i:longint; begin read(s);n:=length(s); if s=‘0‘ then begin write(‘0‘);halt; end; for i:=n downto 1 do p[n-i+1]:=ord(s[i])-48;{把小位数放在前面} end; procedure work; var i,j,x,l:longint; begin for i:=1 to s1 do for j:=1 to s2 do begin l:=a[i]*b[j]; c[i+j-1]:=c[i+j-1]+l; {解决错位相加问题,a、b数组的值正好放在c数组【i+j-1】的位置上,就可以用这个位置原来的数加上新乘出的数解决问题,在运算过程中,a、b数组里面的值不能改变,所以必须要新开一个新数组} end; for i:=1 to k do begin c[i+j]:=c[i+j]+c[i+j-1] div 10; {这一步必须在前面否则下面一步就改变了c数组里面的值} c[i+j-1]:=c[i+j-1] mod 10; end; while c[k]=0 do dec(k);{寻找最高位} x:=c[k]; while x>0 do begin c[k]:=x mod 10; x:=x div 10; inc(k); end;{解决最高位问题} end; procedure output; var i:longint; begin for i:=k-1 downto 1 do write(c[i]); end; begin fillchar(a,sizeof(a),0); fillchar(b,sizeof(b),0);fillchar(c,sizeof(c),0); init(a);readln;s1:=n; init(b);readln; s2:=n; k:=s1+s2; {乘法得出结果后的最高位数为两个位数的因数之和} work;output; end.
高精度·除法
type arr=array[0..10000]of longint; var a,b,c,d:arr;a1,b1,n,j,I,len:longint;s,s1,s2:string; procedure init; begin readln(s);len:=length(s); a1:=pos(‘ ‘,s);s1:=copy(s,1,a1-1);s2:=copy(s,a1+1,len-a1);a1:=length(s1); for i:=1 to a1 do a[i]:=ord(s1[a1-i+1])-48; b[0]:=length(s2); for i:=1 to b[0] do b[i]:=ord(s2[b[0]-i+1])-48; end; function f:boolean; var i:longint; begin if d[0]>b[0] then exit(true); if d[0]<b[0] then exit(false); for i:=d[0] downto 1 do begin if d[i]>b[i] then exit(true) else if d[i]<b[i] then exit(false); end; exit(true); end; procedure change(x:longint); var i:longint; begin inc(d[0]); for i:=d[0] downto 2 do d[i]:=d[i-1]; d[1]:=x; end; function work:boolean; var i:longint; begin if f=false then exit(false); for i:=1 to d[0] do begin d[i+1]:=d[i+1]-1+(d[i]+10-b[i]) div 10; d[i]:=(d[i]+10-b[i])mod 10; end; while (d[d[0]]=0) and(d[0]>1) do dec(d[0]); exit(true); end; procedure output; var i:longint; begin while (c[a1]=0) and(a1>1) do dec(a1); for i:=a1 downto 1 do write(c[i]); writeln; end; begin fillchar(a,sizeof(a),0); fillchar(b,sizeof(b),0); fillchar(c,sizeof(c),0); fillchar(d,sizeof(d),0); init; if a1<b[0] then begin write(‘0...‘); for j:=a1 downto 1 do write(a[j]); halt; end; for j:=a1 downto 1 do begin change(a[j]); while work do inc(c[j]); end; output; end.
高精度·阶乘
{高精度乘单精度} const max=2000; var a:array[1..max] of longint;n,i,j,l,q:longint; begin readln(n); a[1]:=1;l:=1; {l表示前面的数依次的阶乘} for i:=2 to n do begin for j:=1 to l do a[j]:=a[j]*i;{做乘法} for j:=1 to l do begin a[j+1]:=a[j+1]+a[j] div 10; a[j]:=a[j] mod 10;{处理进位问题} while a[l+1]<>0 do begin l:=l+1; a[l+1]:=a[l+1]+a[l] div 10; a[l]:=a[l] mod 10; end; {处理最高位及数位问题} end; end; for i:=l downto 1 do write(a[i]); end.
排序-快速排序
procedure sort(l,r:longint); var i,j,x,y:longint; begin i:=l;j:=r; x:=a[(l+r) div 2]; repeat while a[i]<x do inc(i); while x<a[j] do dec(j); if not(i>j) then begin y:=a[i];a[i]:=a[j];a[j]:=y; inc(i);dec(j); end; until i>j; if l<j then sort(l,j); if i<r then sort(i,r); end;
排序-归并排序
var n,i,ans:longint; a,t:array[0..40005]of longint; procedure mergesort(l,r:longint); var mid,i,j,x:longint; begin if (r-l)<=1 then exit; //分治三步法: //一.划分 mid:=l+(r-l) div 2; i:=l; j:=mid; x:=i; //i,j表示两个合并的序列的首坐标; //x表示当前修改到第几个元素 //递归求解 mergesort(l,mid); mergesort(mid,r); //合并 while (i<mid)or(j<r) do begin if (j>=r)or((i<mid)and(a[i]<=a[j])) then begin t[x]:=a[i]; inc(x); inc(i); end else begin t[x]:=a[j]; inc(x); inc(j); //统计左边大于右边的: //在区间[l,mid)中,共有元素(mid-l)个 //其中进入上个条件的有(i-l)个 //相减得到(mid-i)个 ans:=ans+(mid-i); end; end; for i:=l to r-1 do a[i]:=t[i]; end; begin readln(n); for i:=1 to n do read(a[i]); mergesort(1,n+1); writeln(ans); end.
数位dp
typedef long long ll; int a[20]; ll dp[20][state];//不同题目状态不同 ll dfs(int pos,/*state变量*/,bool lead/*前导零*/,bool limit/*数位上界变量*/)//不是每个题都要判断前导零 { //递归边界,既然是按位枚举,最低位是0,那么pos==-1说明这个数我枚举完了 if(pos==-1) return 1;/*这里一般返回1,表示你枚举的这个数是合法的,那么这里就需要你在枚举时必须每一位都要满足题目条件,也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。不过具体题目不同或者写法不同的话不一定要返回1 */ //第二个就是记忆化(在此前可能不同题目还能有一些剪枝) if(!limit && !lead && dp[pos][state]!=-1) return dp[pos][state]; /*常规写法都是在没有限制的条件记忆化,这里与下面记录状态是对应,具体为什么是有条件的记忆化后面会讲*/ int up=limit?a[pos]:9;//根据limit判断枚举的上界up;这个的例子前面用213讲过了 ll ans=0; //开始计数 for(int i=0;i<=up;i++)//枚举,然后把不同情况的个数加到ans就可以了 { if() ... else if()... ans+=dfs(pos-1,/*状态转移*/,lead && i==0,limit && i==a[pos]) //最后两个变量传参都是这样写的 /*这里还算比较灵活,不过做几个题就觉得这里也是套路了 大概就是说,我当前数位枚举的数是i,然后根据题目的约束条件分类讨论 去计算不同情况下的个数,还有要根据state变量来保证i的合法性,比如题目 要求数位上不能有62连续出现,那么就是state就是要保存前一位pre,然后分类, 前一位如果是6那么这意味就不能是2,这里一定要保存枚举的这个数是合法*/ } //计算完,记录状态 if(!limit && !lead) dp[pos][state]=ans; /*这里对应上面的记忆化,在一定条件下时记录,保证一致性,当然如果约束条件不需要考虑lead,这里就是lead就完全不用考虑了*/ return ans; } ll solve(ll x) { int pos=0; while(x)//把数位都分解出来 { a[pos++]=x%10;//个人老是喜欢编号为[0,pos),看不惯的就按自己习惯来,反正注意数位边界就行 x/=10; } return dfs(pos-1/*从最高位开始枚举*/,/*一系列状态 */,true,true);//刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛 } int main() { ll le,ri; while(~scanf("%lld%lld",&le,&ri)) { //初始化dp数组为-1,这里还有更加优美的优化,后面讲 printf("%lld\n",solve(ri)-solve(le-1)); } }
双哈希
const p1=6662333; p2=100000123; x1=31; x2=29; var n,i,ans:longint; s:ansistring; a,b:array[0..10000]of longint; procedure sort(l,r: longint); var i,j,x,y: longint; begin i:=l; j:=r; x:=a[(l+r) div 2]; repeat while a[i]<x do inc(i); while x<a[j] do dec(j); if not(i>j) then begin y:=a[i];a[i]:=a[j];a[j]:=y; y:=b[i];b[i]:=b[j];b[j]:=y; inc(i); j:=j-1; end; until i>j; if l<j then sort(l,j); if i<r then sort(i,r); end; function hash1:longint; var i:longint; begin hash1:=0; for i:=1 to length(s) do hash1:=(hash1*x1+ord(s[i])-ord(‘a‘))mod p1; end; function hash2:longint; var i:longint; begin hash2:=0; for i:=1 to length(s) do hash2:=(hash2*x2+ord(s[i])-ord(‘a‘)) mod p2; end; begin readln(n); for i:=1 to n do begin readln(s); a[i]:=hash1; b[i]:=hash2; end; sort(1,n); ans:=1; for i:=2 to n do if (a[i]<>a[i-1])or(b[i]<>b[i-1]) then ans:=ans+1; writeln(ans); end.
Tarjan缩点
var next,head,key,next1,head1,key1:array[0..50000]of longint; tot,totc,tott,count,top,a,b,m,n,i,ans,num,j,v,u,x:longint; DFN,Low,stack,q,belong,size:array[0..10000]of longint; flag:array[0..10000]of boolean; procedure add(u,v:longint); begin tot:=tot+1; next[tot]:=head[u]; head[u]:=tot; key[tot]:=v; end; procedure add1(u,v:longint); begin tott:=tott+1; next1[tott]:=head1[u]; head1[u]:=tott; key1[tott]:=v; inc(q[u]); end; procedure Tarjan(u:longint); var i,v,j:longint; begin count:=count+1; DFN[u]:=count; LOW[u]:=count; top:=top+1; stack[top]:=u; flag[u]:=true; i:=head[u]; while (i<>-1) do begin v:=key[i]; if dfn[v]=0 then begin Tarjan(v); if low[v]<low[u] then low[u]:=low[v]; end else if (dfn[v]<low[u])and(flag[v]) then low[u]:=dfn[v]; i:=next[i]; end; if low[u]=dfn[u] then begin inc(totc); repeat j:=stack[top]; top:=top-1; flag[j]:=false; belong[j]:=totc; inc(size[totc]); until j=u; end; end; begin fillchar(next,sizeof(next),-1); fillchar(head,sizeof(head),-1); readln(n,m); ans:=0; for i:=1 to m do begin read(a,b); add(a,b); end; fillchar(flag,sizeof(flag),true); for i:=1 to n do if DFN[i]=0 then Tarjan(i); for u:=1 to n do begin i:=head[u]; while i<>-1 do begin v:=key[i]; if belong[u]<>belong[v] then add1(belong[u],belong[v]); i:=next[i]; end; end; x:=0; for i:=1 to totc do if q[i]=0 then begin x:=x+1; ans:=size[i]; end; if x=1 then writeln(ans) else writeln(0); end.
Tarjan求LCA
var head,next,key,head1,key1,size,next1,fa,ans:array[0..1000010]of longint; vis:array[0..1000010]of boolean; tot1,tot,T,i:longint; procedure add(x,y:longint); begin tot:=tot+1; next[tot]:=head[x]; head[x]:=tot; key[tot]:=y; end; procedure add1(x,y,z:longint); begin tot1:=tot1+1; next1[tot1]:=head1[x]; head1[x]:=tot1; key1[tot1]:=y; size[tot1]:=z; end; function getfather(x:longint):longint; begin if fa[x]=x then exit(x) else begin fa[x]:=getfather(fa[x]); exit(fa[x]); end; end; procedure Tarjan_LCA(u,pre:longint); var i,v:longint; begin fa[u]:=u; i:=head[u]; while i>0 do begin v:=key[i]; if v=pre then begin i:=next[i]; continue; end; Tarjan_LCA(v,u); fa[v]:=u; i:=next[i]; end; vis[u]:=true; i:=head1[u]; while i>0 do begin v:=key1[i]; if vis[v]=false then begin i:=next1[i]; continue; end; ans[size[i]]:=getfather(v); i:=next1[i]; end; end; procedure make; var i,x,y,n,m,s:longint; begin fillchar(ans,sizeof(ans),0); readln(n,m,s); for i:=1 to n-1 do begin readln(x,y); add(x,y);add(y,x); end; for i:=1 to m do begin readln(x,y); add1(x,y,i); add1(y,x,i); end; tarjan_LCA(s,0); for i:=1 to m do writeln(ans[i]); end; begin make; end.
倍增求LCA
var n,m,s,i,u,v,a,b,tot,j:longint; next,head,key,deep:array[0..1000000]of longint; father:array[0..500000,0..20]of longint; function swap(var a,b:longint):longint; var t:longint; begin t:=a; a:=b; b:=t; end; procedure add(u,v:longint); begin inc(tot); next[tot]:=head[u]; head[u]:=tot; key[tot]:=v; end; procedure dfs(x:longint); var i:longint; begin deep[x]:=deep[father[x,0]]+1; i:=0; while father[x,i]>0 do begin father[x,i+1]:=father[father[x,i],i]; inc(i); end; i:=head[x]; while i>0 do begin if deep[key[i]]=0 then begin father[key[i],0]:=x; dfs(key[i]); end; i:=next[i]; end; end; function LCA(x,y:longint):longint; var i:longint; begin if deep[x]>deep[y] then swap(x,y); for i:=19 downto 0 do if (deep[father[y,i]]>=deep[x]) then y:=father[y,i]; if x=y then exit(y); for i:=19 downto 0 do if father[y,i]<>father[x,i] then begin y:=father[y,i]; x:=father[x,i]; end; exit(father[x,0]); end; begin tot:=0; read(n,m,s); for i:=1 to n-1 do begin read(u,v); add(u,v); add(v,u); end; dfs(s); for i:=1 to m do begin read(a,b); writeln(LCA(a,b)); end; end.
暂且更新到这里。