2019.2.15

长沙集训 $ Day 5 $

今天的题目还是比较简单的,但是我的程序却出现了许多严重的 $ bug $ ,最终让我获得了 $ 20 $ 分的“好成绩”…


T1 modeq

这题可以说是扩展欧几里得算法的板子题了,分别套用一次 $ exgcd $ 求出最小 $ x,y $ ,然而我调试时把绝对值删了,后来又忘了改回去,最后 $ 100 \to 0 $

求解方法:

对于方程 $ a \cdot x + b \cdot y = c $

  1. 只有在 $ c \ \ mod \ \ gcd(a,b) = 0 $ 时,方程才有解
  2. 若 $ exgcd $ 解出的 $ x $ 为负值,可用 $ x=(x+abs(b*n)) \ \ mod \ \ b $ 的方式将其变为正值
  3. $ \frac{(a \cdot x)}{m} +\frac{(b \cdot y)}{m} =\frac{c}{m} $ 此方程的解与原方程相同,所以可以先将方程化简在进行处理

程序

program modeq;
var t,x,y,gd:int64;

function gcd(a,b:int64):int64;
begin
  if b=0 then exit(a);
  gcd:=gcd(b,a mod b);
end;

procedure exgcd(a,b,c:int64);
var t:int64;
begin
  if b=0 then begin
    x:=c div a;
    y:=0;
    exit;
  end;
  exgcd(b,a mod b,c);
  t:=x;
  x:=y;
  y:=t-(a div b)*y;
end;

procedure main;
var i:longint;
    a,b,c,ans_x,ans_y:int64;
begin
  for i:=1 to t do begin
    read(a,b,c);
    gd:=gcd(a,b);
    if c mod gd<>0 then begin
      writeln('No');
      continue;
    end;
    a:=a div gd;
    b:=b div gd;
    c:=c div gd;  
    if c mod b=0 then write('0',' ',c div b,' ')
      else begin
        exgcd(a,b,c);  
        ans_x:=(x+abs(b*1000000000)) mod b;
        ans_y:=(c-a*ans_x) div b;
        write(ans_x,' ',ans_y,' ');
      end;
    if c mod a=0 then writeln(c div a,' ','0')
      else begin
        exgcd(b,a,c);  
        ans_y:=(x+abs(a*1000000000)) mod a;
        ans_x:=(c-b*ans_y) div a;
        writeln(ans_x,' ',ans_y);
    end;
  end;
end;

begin
  read(t);
  main;
end.

T2 买卖

开始以为是个毒瘤 $ DP $ ,后来发现是贪心,只要找到能把当前可以买入的最便宜的物品卖出赚钱的商店就把物品卖出,如果之后又更优秀的方案,就进行更改:

编号 买入 卖出
A $ 7 $ $ 1 $
B $ 10 $ $ 8 $
C $ 12 $ $ 12 $
  1. 在 $ A $ 处买入物品,在 $ B $ 出卖出价值 $ w=8-7=1 $ ,并将 $ A $ 处物品价格改为 $ 8 $
  2. 发现在 $ C $ 出卖出是更优方案,相当于重新将物品买入,并在 $ C $ 处卖出,价值 $ w=12-8+1=12-7=5 $
  3. 可买入物品的最小值可用小根堆/最小值线段树等数据结构维护

一定注意初始化

程序

program buy;
var
   tree:array[0..400005]of int64;
   a:array[0..100005,1..2]of int64;
   n,tar,sum:longint;

function min(a,b:int64):int64;
begin
  if a<b then exit(a) else exit(b);
end;

procedure re;
var i:longint;
begin
  for i:=1 to n do read(a[i,1]);
  for i:=1 to n do read(a[i,2]);
end;

function search(l,r,t,k:longint):boolean;
var mid:longint;
begin
  search:=false;
  if l=r then begin   
    tree[t]:=k;
    exit(true);
  end;
  mid:=(l+r) shr 1;
  if tree[t shl 1 + 1]=tar then search:=search(mid+1,r,t shl 1 + 1,k);
  if search=false then search:=search(l,mid,t shl 1,k);
  tree[t]:=min(tree[t shl 1],tree[t shl 1 + 1]);
  exit(search);
end;

procedure change(l,r,t,x,k:longint);
var mid:longint;
begin
  if (l=r) and (l=x) then begin
    tree[t]:=k;
    exit;
  end;
  mid:=(l+r) shr 1;
  if x<=mid then change(l,mid,t shl 1,x,k);
  if x>=mid+1 then change(mid+1,r,t shl 1 + 1,x,k);
  tree[t]:=min(tree[t shl 1],tree[t shl 1 + 1]);
end;

procedure main;
var x,ans:int64;
    i:longint;
begin
  ans:=0;
  for i:=1 to n do begin
    change(1,n,1,i,a[i,1]);
    if tree[1]<a[i,2] then begin
      tar:=tree[1];   
      inc(ans,a[i,2]-tree[1]);
      search(1,n,1,a[i,2]);
    end;
  end;
  writeln(ans);
end;

begin
  fillchar(tree,sizeof(tree),$5f);
  read(n);
  re;
  main;
end.

T3 投资

这题看起来是个 $ DP $ ,其实是前缀和+小根堆。

两题都是小根堆,可以复制代码了!!!

设$ [l,r] $ 是连续天数的范围

$ ans=max(sum[i]-min(sum[j])) \ \ \ \ j \in [i-r,i-l]$

程序

program invest;
var
   a:array[0..100005]of int64;
   heap:array[0..100005,1..2]of int64;
   n,l,r,tot:longint;

function max(a,b:int64):int64;
begin
  if a>b then exit(a) else exit(b);
end;

function min(a,b:int64):int64;
begin
  if a<b then exit(a) else exit(b);
end;

procedure re;
var i:longint;
begin
  for i:=1 to n do read(a[i]);
end;

procedure insert(q,p:int64);
var i,j,t:int64;
begin
  inc(tot);   
  heap[tot,1]:=q;
  heap[tot,2]:=p;
  i:=tot;
  while i shr 1<>0 do begin
    j:=i shr 1;
    if heap[j,1]>heap[i,1] then begin
      t:=heap[j,1];
      heap[j,1]:=heap[i,1];
      heap[i,1]:=t;
      t:=heap[j,2];
      heap[j,2]:=heap[i,2];
      heap[i,2]:=t;
    end
      else break;
    i:=j;
  end;
end;

procedure del;
var i,j,t:longint;
begin
  heap[1]:=heap[tot];
  dec(tot);
  i:=1;
  while i shl 1<=tot do begin
    j:=i shl 1;
    if (j+1<=tot) and (heap[j,1]>heap[j+1,1]) then inc(j);
    if heap[j,1]<heap[i,1] then begin
      t:=heap[j,1];
      heap[j,1]:=heap[i,1];
      heap[i,1]:=t;
      t:=heap[j,2];
      heap[j,2]:=heap[i,2];
      heap[i,2]:=t;
    end
      else break;
    i:=j;
  end;
end;

procedure main;
var sum:array[0..100005]of int64;
    ans:int64;
    i:longint;
begin
  fillchar(sum,sizeof(sum),0);
  ans:=-10000000000;
  sum[1]:=a[1];
  if l<=1 then ans:=sum[1];
  for i:=2 to n do begin
    sum[i]:=sum[i-1]+a[i];   
    if i-l>0 then insert(sum[i-l],i-l);   
    while i-heap[1,2]>r do del;
    if i>=l then ans:=max(ans,sum[i]-heap[1,1]);
  end;
  writeln(ans);  
end;

begin
  fillchar(heap,sizeof(heap),$5f);
  read(n,l,r);
  re;
  main;
end.

T4 游戏

先用 $ manacher $ 求出以每个位置为中心的最长回文串的半径,之后进行统计,半径为 $ t $ 的回文串个数 $ sum[t] $ 为半径大于等于 $ t $ 的回文串个数之和。统计个数后用快速幂计算答案,注意取模。

程序

program rehearse;

const p=19930726;

var
   f:array[0..2000005]of longint;
   sum:array[0..2000005]of int64;
   s:array[0..2000005]of char;
   len,n,k:int64;

function min(a,b:longint):longint;
begin
  if a<b then exit(a) else exit(b);
end;

procedure re;
var i:longint;
    p:ansistring;
begin
  readln(p);
  len:=1;
  s[0]:='#';
  s[len]:='#';
  for i:=1 to n do begin
    inc(len);
    s[len]:=p[i];
    inc(len);
    s[len]:='#';
  end;
end;

procedure manacher;
var i,mid,mx:longint;
begin
  mid:=1;
  mx:=1;
  for i:=1 to len do begin
    if i<mx then f[i]:=min(mx-i,f[mid*2-i])
      else f[i]:=1;
    while s[i-f[i]]=s[i+f[i]] do inc(f[i]);
    if f[i]+i>mx then begin
      mx:=f[i]+i;
      mid:=i;
    end;
  end;
  for i:=1 to len do
    if s[i]<>'#' then inc(sum[f[i]]);
end;

function mul(q,k:int64):int64;
var t:int64;
begin
  t:=q;
  mul:=1;
  while k<>0 do begin
    if k and 1<>0 then mul:=(mul mod p)*(t mod p) mod p;
    t:=(t mod p)*(t mod p) mod p;
    k:=k shr 1;
  end;
end;

procedure main;
var i:longint;
    ans,t:int64;
begin
  ans:=1;
  t:=0;
  sum[(len div 2) + 3]:=0;
  for i:=(len div 2) + 2 downto 1 do begin
    sum[i]:=sum[i]+sum[i+1];      
    if (i-1) mod 2=1 then begin
      inc(t,sum[i]);
      if t<=k then ans:=(ans mod p)*(mul(i-1,sum[i]) mod p) mod p
        else begin
          ans:=(ans mod p)*(mul(i-1,k-t+sum[i]) mod p) mod p;
          break;
        end;
    end;
  end;
  if t<k then writeln('-1')
    else writeln(ans);
end;

begin
  readln(n,k);
  re;
  manacher;
  main;
end.