最近的几道好题

由于题目不是自己人出的,所以题面就不给了吧

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.


Day2

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 枚举+验证

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值