由于题目不是自己人出的,所以题面就不给了吧
CSDN贴代码不能缩到一起了,所以这篇文章会显得比较冗长
Day1:
1.Hash or 后缀数组
大概的做法:枚举答案长度,暴力匹配验证答案即可
代码:
program syj;
type
arr=array[0..200005]of longint;
var
m,task,n,i,j,k,l,z,ans,o:longint;
aa,a,b,c,sa,tmp,rank:arr;
h:array[0..19]of arr;
cc:char;
tm:real;
procedure sort(var a:arr);
begin
fillchar(c,sizeof(c),0);
for i:=1 to n do inc(c[a[i]+1]);
for i:=1 to o do inc(c[i],c[i-1]);
for i:=1 to n do begin
inc(c[a[sa[i]]]);
tmp[c[a[sa[i]]]]:=sa[i];
end;
for i:=1 to n do sa[i]:=tmp[i];
end;
procedure getrank;
begin
o:=0;
for i:=1 to n do begin
inc(o,ord((a[sa[i]]<>a[sa[i-1]])or(b[sa[i]]<>b[sa[i-1]])));
rank[sa[i]]:=o;
end;
end;
function ask(i,j:longint):longint;
begin
if i=j then exit(n-i+1);
i:=rank[i];j:=rank[j];
if i>j then begin
z:=i;i:=j;j:=z;
end;
inc(i);
k:=trunc(ln(j-i+1)/ln(2)+1e-8);
if h[k,i]<h[k,j-1<<k+1] then ask:=h[k,i]
else ask:=h[k,j-1<<k+1];
end;
function check1(i:longint):boolean;
var j,k,l:longint;
begin
j:=ask(1,i);
k:=i+j;
if k<=i then exit(false);
l:=ask(k+1,j+1);
if j+l>k-1 then l:=k-1-j;
if k+l=n then exit(true);
if j+l<i-1 then exit(false);
if j+l+1<k then exit(false);
exit(k+l+ask(k+1,k+l+1)=n);
end;
function check2(i:longint):boolean;
var j:longint;
begin
j:=ask(1,i);
if (j=0)or(j+1>=i) then exit(false);
exit(i+j+ask(j+2,i+j)-1=n);
end;
procedure debug;
var i,j:longint;
begin
for i:=1 to n do begin
for j:=1 to n do
write(ask(i,j),' ');
writeln;
end;
end;
function check3(i:longint):boolean;
begin
check3:=(i>2)and(ask(i,2)=n-i+1);
end;
begin
assign(input,'naj.in');reset(input);
assign(output,'naj.out');rewrite(output);
readln(task);
for task:=1 to task do begin
read(n,cc);
fillchar(a,sizeof(a),0);
fillchar(b,sizeof(b),0);
fillchar(rank,sizeof(rank),0);
fillchar(sa,sizeof(sa),0);
fillchar(aa,sizeof(aa),0);
fillchar(h,sizeof(h),0);
for i:=1 to n do begin
read(cc);
a[i]:=ord(cc)-96;
aa[i]:=a[i];
b[i]:=0;
end;
readln;
for i:=1 to n do sa[i]:=i;
o:=26;
sort(a);getrank;
l:=1;
while o<>n do begin
for i:=1 to n do begin
a[i]:=rank[i];
if i+l<=n then b[i]:=rank[i+l]
else b[i]:=0;
end;
sort(b);sort(a);getrank;
l:=l*2;
end;
l:=0;
for i:=1 to n do begin
if l>0 then dec(l);
if rank[i]=1 then continue;
j:=sa[rank[i]-1];
while (i+l<=n)and(j+l<=n)and(aa[i+l]=aa[j+l]) do inc(l);
h[0,rank[i]]:=l;
end;
m:=trunc(ln(n)/ln(2)+1e-8);
for j:=1 to m do
for i:=1 to n-1<<j+1 do
if h[j-1,i]<h[j-1,i+1<<(j-1)] then
h[j,i]:=h[j-1,i]
else
h[j,i]:=h[j-1,i+1<<(j-1)];
ans:=n-1;
for i:=2 to n do
if check2(i)or check3(i) then begin
ans:=i-2;break;
end else
if check1(i) then begin
ans:=i-1;break;
end;
writeln(ans);
end;
close(input);close(output);
end.
2.复合方法(按答案与sqrt(n)的大小分情况讨论)、分块、排序+树状数组
答案 > sqrt(n)时是很好办的,分块+二分查找即可
而答案 < sqrt(n)时相对麻烦一点,由于空间的问题,不能直接开sqrt(n)*n的预处理数组,我们就从1~sqrt(n)枚举答案,得到O(n)个区间后再用排序+树状数组更新一遍所有询问的答案
ps:我写的程序速度极慢无比,极限数据得跑40s。囧。还有一点,pascal的类还挺好用的。
代码:
program syj;
uses
math,sysutils;
type
lisanhua=object
a:array[0..500001]of longint;
t:longint;
procedure put(i:longint);
procedure sort(l,r:longint);
function get(i:longint):longint;
end;
var
s,n,m,i,j,k,t,z,q:longint;
a,b,l,r,p,lmi,lma,rmi,rma,lc,rc:array[0..50001]of longint;
u,v,w:array[0..500001]of longint;
ans,x,y:array[0..200001]of longint;
f:array[0..500001]of boolean;
o:lisanhua;
tm:real;
procedure lisanhua.put(i:longint);
begin
inc(t);a[t]:=i;
end;
procedure lisanhua.sort(l,r:longint);
var i,j:longint;
begin
i:=l;j:=r;a[0]:=a[(l+r)>>1];
repeat
while a[i]<a[0] do inc(i);
while a[j]>a[0] do dec(j);
if i<=j then begin
z:=a[i];a[i]:=a[j];a[j]:=z;
inc(i);dec(j);
end;
until i>j;
if l<j then sort(l,j);
if i<r then sort(i,r);
end;
function lisanhua.get(i:longint):longint;
var l,r,m:longint;
begin
l:=1;r:=t;
while l<r do begin
m:=(l+r)>>1;
if i<=a[m] then r:=m
else l:=m+1;
end;
end;
procedure up(i,k:longint);
begin
if k>ans[i] then ans[i]:=k;
end;
function ok(l1,r1,l2,r2:longint):boolean;
begin
ok:=not((l1>r2)or(l2>r1));
end;
function findl(i,k:longint):longint;
var l,r,m:longint;
begin
if i=0 then exit(0);
l:=lc[i];r:=rc[i];
while l<r do begin
m:=(l+r)>>1;
if ok(x[k],y[k],rma[m],rmi[m]) then r:=m
else l:=m+1;
end;
if not ok(x[k],y[k],rma[l],rmi[l]) then inc(l);
findl:=rc[i]-l+1;
end;
function findr(i,k:longint):longint;
var l,r,m:longint;
begin
if i>t then exit(0);
l:=lc[i];r:=rc[i];
while l<r do begin
m:=(l+r+1)>>1;
if ok(x[k],y[k],lma[m],lmi[m]) then l:=m
else r:=m-1;
end;
if not ok(x[k],y[k],lma[l],lmi[l]) then dec(l);
findr:=l-lc[i]+1;
end;
procedure add(x,y,z:longint);
begin
inc(q); u[q]:=x;v[q]:=y;w[q]:=z;
end;
procedure sort(l,r:longint);
var i,j:longint;
begin
i:=l;j:=r;u[0]:=u[(l+r)>>1];w[0]:=w[(l+r)>>1];
repeat
while (u[i]<u[0])or(u[i]=u[0])and(w[i]<w[0]) do inc(i);
while (u[j]>u[0])or(u[j]=u[0])and(w[j]>w[0]) do dec(j);
if i<=j then begin
z:=u[i];u[i]:=u[j];u[j]:=z;
z:=v[i];v[i]:=v[j];v[j]:=z;
z:=w[i];w[i]:=w[j];w[j]:=z;
inc(i);dec(j);
end;
until i>j;
if l<j then sort(l,j);
if i<r then sort(i,r);
end;
procedure update(i:longint);
begin
while i>0 do begin
f[i]:=true;
i:=i-i and-i;
end;
end;
function ask(i:longint):longint;
begin
while i<=o.t do begin
if f[i] then exit(1);
i:=i+i and-i;
end;
ask:=0;
end;
procedure work(len:longint);
var i:longint;
begin
q:=0;
for i:=1 to n-len+1 do add(l[i],r[i],0);
for i:=1 to m do if len>ans[i] then add(y[i],x[i],i);
sort(1,q);
fillchar(f,sizeof(f),0);
for i:=1 to q do
if w[i]=0 then update(v[i])
else up(w[i],ask(v[i])*len);
end;
begin
tm:=time;
assign(input,'kan.in');reset(input);
assign(output,'kan.out');rewrite(output);
readln(n,m);
o.t:=0;
for i:=1 to n do begin
readln(a[i],b[i]);
o.put(a[i]);o.put(b[i]);
end;
for i:=1 to m do begin
readln(x[i],y[i]);
o.put(x[i]);
o.put(y[i]);
end;
o.sort(1,o.t);
for i:=1 to n do begin
a[i]:=o.get(a[i]);
b[i]:=o.get(b[i]);
end;
for i:=1 to m do begin
x[i]:=o.get(x[i]);
y[i]:=o.get(y[i]);
end;
// writeln('lisanhua : ',(time-tm)*24*3600:0:2);
tm:=time;
s:=min(n,max(trunc(sqrt(n)),3));
j:=1;t:=1;
for i:=1 to n do begin
p[i]:=t;
if j=s then begin
j:=1;inc(t)
end else inc(j);
end;
if j=1 then dec(t);
for i:=1 to n do
if p[i]<>p[i-1] then begin
lc[p[i]]:=i;
lma[i]:=a[i];
lmi[i]:=b[i];
end else begin
lma[i]:=max(a[i],lma[i-1]);
lmi[i]:=min(b[i],lmi[i-1]);
end;
for i:=n downto 1 do
if p[i]<>p[i+1] then begin
rc[p[i]]:=i;
rma[i]:=a[i];
rmi[i]:=b[i];
end else begin
rma[i]:=max(a[i],rma[i+1]);
rmi[i]:=min(b[i],rmi[i+1]);
end;
lc[t+1]:=n+1;rc[t+1]:=n+1;
lma[n+1]:=o.t+1;lmi[n+1]:=o.t+1;
for i:=1 to m do begin
k:=1;
for j:=1 to t+1 do
if not ok(x[i],y[i],lma[rc[j]],lmi[rc[j]]) then begin
z:=findl(k-1,i)+rc[j-1]-lc[k]+1+findr(j,i);
up(i,z);
k:=j+1;
end;
end;
// writeln('>sqrt(n) : ',(time-tm)*24*3600:0:2);
tm:=time;
l:=a;r:=b;
work(1);
for i:=1 to s-1 do begin
for j:=1 to n-i do begin
l[j]:=max(l[j],a[i+j]);
r[j]:=min(r[j],b[i+j]);
end;
work(i+1);
end;
// writeln('<sqrt(n) : ',(time-tm)*24*3600:0:2);
for i:=1 to m do
writeln(ans[i]);
close(input);close(output);
end.
3.插头DP or 费用流 or 最大流
插头DP的方法,讨论的时候好像是DYF讲了
费用流的方法题解里有
实际上这道题是可以直接用最大流求解的,出题人竟然没想到?!
思想跟费用流的方法是一样的,基于度数分配
用(i, j, L, R)表示i向j连一条下界为L,上界为R的边,s表示源,t表示汇
具体方法:
先黑白染色
(s, 黑点, 在边界上为1不在边界上为2, 2) (*)
(黑点, 相邻的白点, 0, 1)
(白点, t, 在边界上为1不在边界上为2, 2) (*)
求最大流
最后答案就是(*)边中只流了1的流量的边数除以2
代码:
program syj;
const
fx:array[1..4]of longint=(-1,0,1,0);
fy:array[1..4]of longint=(0,1,0,-1);
var
flow,n,m,i,j,k,x,y,ans,s,t,ss,tt,kk,e:longint;
q,d,pre,fir,h:array[0..300]of longint;
next,point,w:array[0..2000]of longint;
o:array[0..2000]of boolean;
a:array[0..15,0..15]of longint;
cc:char;
function bfs:boolean;
var i,j,k,st,ed:longint;
begin
fillchar(d,sizeof(d),$FF);
st:=0;ed:=1;q[1]:=ss;d[ss]:=0;
while st<ed do begin
inc(st);i:=q[st];j:=h[i];
while j<>0 do begin
k:=point[j];
if (w[j]>0)and(d[k]=-1) then begin
d[k]:=d[i]+1;inc(ed);q[ed]:=k;
if k=tt then exit(true);
end;
j:=next[j];
end;
end;
bfs:=false;
end;
function ff(i:longint):string;
begin
if i=s then ff:='s' else
if i=t then ff:='t' else
if i=ss then ff:='ss' else
if i=tt then ff:='tt' else str(i,ff);
end;
procedure imp;
var i,j:longint;
begin
i:=pre[tt];j:=maxlongint;
// write(ff(tt),' ');
while i<>0 do begin
// write(ff(i),' ');
if w[fir[i]]<=j then begin j:=w[fir[i]];kk:=i end;
i:=pre[i];
end;
// writeln(': ',j);
i:=pre[tt];
while i<>0 do begin
dec(w[fir[i]],j);
inc(w[fir[i]xor 1],j);
i:=pre[i];
end;
dec(flow,j);
end;
procedure dfs;
var i,j,k:longint;ok:boolean;
begin
for i:=1 to tt do fir[i]:=h[i];
i:=ss;pre[ss]:=0;
while i<>0 do begin
j:=fir[i];ok:=true;
while j<>0 do begin
k:=point[j];
if (w[j]>0)and(d[k]=d[i]+1) then begin
pre[k]:=i;fir[i]:=j;ok:=false;i:=k;
if k=tt then begin imp;i:=kk end;
break;
end;
j:=next[j];
end;
if ok then begin
d[i]:=-1;i:=pre[i];
end;
end;
end;
procedure link(x,y,z:longint;oo:boolean);
begin
if z=0 then exit;
inc(e); next[e]:=h[x];point[e]:=y;w[e]:=z;o[e]:=oo;h[x]:=e;
inc(e); next[e]:=h[y];point[e]:=x;w[e]:=0;h[y]:=e;
end;
procedure add(i,j,k,l:longint);
begin
link(ss,j,k,false);
link(i,tt,k,false);
link(i,j,l-k,k=1);
inc(flow,k);
end;
begin
assign(input,'snake.in');reset(input);
assign(output,'snake.out');rewrite(output);
while not seekeof do begin
inc(n);
m:=0;
while not seekeoln do begin
inc(m);
read(cc);
if cc='.' then begin
inc(tt);
a[n,m]:=tt;
end;
end;
readln;
end;
s:=tt+1;t:=s+1;ss:=t+1;tt:=ss+1;e:=1;
for i:=1 to n do
for j:=1 to m do if a[i,j]>0 then
if odd(i+j) then begin
add(s,a[i,j],2-ord((i=1)or(j=1)or(i=n)or(j=m)),2);
for k:=1 to 4 do begin
x:=i+fx[k];y:=j+fy[k];
if a[x,y]>0 then link(a[i,j],a[x,y],1,false);
end;
end else
add(a[i,j],t,2-ord((i=1)or(j=1)or(i=n)or(j=m)),2);
link(t,s,maxlongint,false);
while bfs do dfs;
if flow>0 then begin
writeln(-1);
close(input);close(output);
halt;
end;
ss:=s;tt:=t;
w[e]:=0;w[e-1]:=0;
while bfs do dfs;
for i:=2 to e do
if o[i]and(w[i]=1) then
inc(ans);
writeln(ans>>1);
close(input);close(output);
end.
1.离散化、匹配
通过很麻烦的预处理之后就是一个裸的最优匹配问题了
直接KM可以过,也可以排序后匈牙利
我这道题的代码过于丑陋就不贴了吧
2.枚举、凸包
枚举一条分界线,左边求个凸包右边求个凸包,更新答案即可
需要注意一下多点共线的情况
3.数论
可以发现ans = (x - 1) ^ n,然后问题就转化成求形如a ^ b mod c = d的所有的a
具体求法见下一篇文章
代码:
program syj;
uses
math;
const
mo=1<<18-1;
type
Thash=object
t:longint;
ne,b:array[0..100000]of longint;
a:array[0..100000]of int64;
h:array[0..mo+1]of longint;
procedure hash(x:int64;y:longint);
function find(x:int64):longint;
procedure clear;
procedure sort;
end;
var
b,c,d,p,q,x,y:int64;
e:array[0..20]of int64;
i,j:longint;
o:Thash;
procedure Thash.hash(x:int64;y:longint);
var i,j:longint;
begin
i:=x and mo;j:=h[i];
while j<>0 do begin
if a[j]=x then exit;
j:=ne[j];
end;
inc(t); ne[t]:=h[i];a[t]:=x;b[t]:=y;h[i]:=t;
end;
function Thash.find(x:int64):longint;
var i,j:longint;
begin
i:=x and mo;j:=h[i];
while j<>0 do begin
if a[j]=x then exit(b[j]);
j:=ne[j];
end;
exit(-1);
end;
procedure Thash.clear;
begin
t:=0;
fillchar(h,sizeof(h),0);
end;
procedure Thash.sort;
var i,j,z:longint;
begin
for i:=1 to t-1 do
for j:=i+1 to t do
if a[i]>a[j] then begin
z:=a[i];a[i]:=a[j];a[j]:=z;
end;
end;
function prime(i:int64):boolean;
var j:longint;
begin
if (i=1)or not odd(i) then exit(false);
for j:=2 to trunc(sqrt(i)) do
if i mod j=0 then
exit(false);
prime:=true;
end;
function mul(a,b:int64):int64;
begin
if b=0 then mul:=1 else begin
mul:=mul(a,b>>1);
mul:=mul*mul mod c;
if odd(b) then
mul:=mul*a mod c;
end;
end;
function root(n:int64):int64;
var ok:boolean;
begin
if n=2 then root:=1;
for i:=2 to n do begin
ok:=true;
for j:=1 to e[0] do
if mul(i,(c-1)div e[j])=1 then begin
ok:=false;break;
end;
if ok then exit(i);
end;
end;
procedure devide(c:int64);
begin
for i:=2 to trunc(sqrt(c)) do
if c mod i=0 then begin
inc(e[0]);e[e[0]]:=i;
while c mod i=0 do c:=c div i;
end;
if c>1 then begin
inc(e[0]);e[e[0]]:=c;
end;
end;
function giant(a,c,d:int64):int64;
var z,s:int64; j,k:longint;
begin
z:=1; k:=ceil(sqrt(c));
for i:=0 to k-1 do begin
o.hash(z,i);
z:=z*a mod c;
end;
s:=1;
for i:=0 to k-1 do begin
j:=o.find(mul(s,c-2)*d mod c);
if j<>-1 then exit(i*k+j);
s:=s*z mod c;
end;
giant:=-1;
end;
function exgcd(a,b:int64;var x,y:int64):int64;
begin
if b=0 then begin
x:=1;y:=0;
exgcd:=a;
end else begin
exgcd:=exgcd(b,a mod b,y,x);
y:=y-a div b*x;
end;
end;
procedure solve(a,b,c:int64);
var u,v:int64;
begin
d:=exgcd(a,b,u,v);
if c mod d<>0 then exit;
b:=b div d;
u:=c div d*u;
x:=(u mod b+b)mod b;
y:=b;
if x=0 then inc(x,b);
end;
begin
assign(input,'wa.in');reset(input);
assign(output,'wa.out');rewrite(output);
readln(b,d,c);
if not prime(c) then c:=2;
devide(c-1);
p:=root(c);
q:=giant(p,c,d);
solve(b,c-1,q);
o.clear;
for i:=0 to d-1 do begin
j:=mul(p,x);
o.hash(mul(j,b-1)*(j+1)mod c,j);
inc(x,y);
end;
o.sort;
writeln(o.t);
for i:=1 to o.t do write(o.a[i],' ');
close(input);close(output);
end.
Day3
1.容斥原理 or 打表找规律
d(n) = n mod 9,然后枚举d(n),解同余方程 + 容斥原理 or 打表找规律即可
2.二分答案+上下界网络流
感觉上这道题跟THSC的第二题差不多
先二分答案
由于∑a是常数,所以将绝对值符号拆掉后,就是一个分配流量的上下界网络流模型,判断是否有解即可
3.最短路
POI原题
求log(n)次最短路的方法加一点常数优化是可以过的
实际上可以只求一次最短路。
在求最短路的同时顺便求一个次短路,记录最短路是由哪条源点直接连出去的边更新过来的,然后枚举从那条边回到源点更新答案时分情况讨论一下即可
Day4
1.构造
首先可以发现排完序后的所有串的最后一位构成的新序列的字母组成是和原串一样的
然后可以发现排完序后的所有串的最后一位在原串中的后一个就是排完序后的所有串的第一位
而第i个“第一位”实际上就是原串的字典序第i大的后缀对应的字母
然后扫一遍即可得到答案
需要注意的是原串可能有循环节,此时最后跑出来的会是几个独立的环
2.贪心+堆
可以发现当前权值最大的点一定会在它的父亲选完后马上选,那么就可以将这个点和他父亲合并
列一列式子之后发现这个新点的权值等价于这一块点的权值的算术平均数
用个堆每次拿一个权值最大的点即可
3.上下界网络流
裸题
Day5
1.图论
2.矩阵DP
3.(提交答案题)手玩 or 调整 or 枚举+验证