长沙集训 $ Day 2 $ 2019-08-20
得分: $ 105/300 $
T1 $ mine $
基本思路
此题可以使用 $ Dp $ 解决,设 $ Dp[i,j,k] $ 表示当前处理到第 $ i $ 位,第 $ i $ 位字符为 $ j $ 类型,第 $ i-1 $ 为符号为 $ k $ 类型时的方案数。
$ s_i=* \ \to \ j,k=1 \ \ \ \ \ \ \ \ \ 0 \ \to \ j,k=2 \ \ \ \ \ \ \ \ \ 1 \ \to \ j,k=3 \ \ \ \ \ \ \ \ \ 2 \ \to \ j,k=4 \ \ \ \ \ \ \ \ \ ? \ \to \ j,k=0 $
状态可从 $ Dp[i-1,k,l] $ 转移得到,其中要注意各种细节问题。
程序
program mine;
const p=1000000007;
var
s:array[0..1000005]of longint;
f:array[0..1000005,1..4,1..4]of longint;
n:longint;
function min(a,b:longint):longint;
begin
if a<b then exit(a)
else exit(b);
end;
procedure re;
var s1:ansistring;
i:longint;
begin
readln(s1);
s1:=s1;
n:=length(s1);
for i:=1 to n do
case s1[i] of
'*':s[i]:=1;
'0':s[i]:=2;
'1':s[i]:=3;
'2':s[i]:=4;
'?':s[i]:=0;
end;
end;
procedure main;
var i,j,k:longint;
ans:int64;
begin
ans:=0;
if s[1]=0 then begin
f[1,1,1]:=1;
f[1,2,2]:=1;
f[1,3,2]:=1;
end
else if s[1]=1 then f[1,1,1]:=1
else if s[1]=2 then f[1,2,2]:=1
else if s[1]=3 then f[1,3,2]:=1;
for i:=2 to n do
for j:=1 to 4 do
for k:=1 to 4 do
if ((s[i]=j) or (s[i]=0)) and ((s[i-1]=k) or (s[i-1]=0)) then begin
if (j=1) and (k=1) then f[i,j,k]:=(f[i,j,k]+f[i-1,k,1]+f[i-1,k,3]+f[i-1,k,4]) mod p;
if (j=1) and (k=3) then f[i,j,k]:=(f[i,j,k]+f[i-1,k,2]+f[i-1,k,3]+f[i-1,k,4]) mod p;
if (j=1) and (k=4) then f[i,j,k]:=(f[i,j,k]+f[i-1,k,1]) mod p;
if (j=2) and (k=2) then f[i,j,k]:=(f[i,j,k]+f[i-1,k,2]+f[i-1,k,3]+f[i-1,k,4]) mod p;
if (j=2) and (k=3) then f[i,j,k]:=(f[i,j,k]+f[i-1,k,1]) mod p;
if (j=3) and (k=1) then f[i,j,k]:=(f[i,j,k]+f[i-1,k,1]+f[i-1,k,3]+f[i-1,k,4]) mod p;
if (j=3) and (k=2) and (i<>n) then f[i,j,k]:=(f[i,j,k]+f[i-1,k,2]+f[i-1,k,3]+f[i-1,k,4]) mod p;
if (j=3) and (k=3) and (i<>n) then f[i,j,k]:=(f[i,j,k]+f[i-1,k,1]) mod p;
if (j=4) and (k=1) and (i<>n) then f[i,j,k]:=(f[i,j,k]+f[i-1,k,1]+f[i-1,k,3]+f[i-1,k,4]) mod p;
end;
for i:=1 to 4 do
for j:=1 to 4 do
ans:=(ans+f[n,i,j]) mod p;
writeln(ans);
end;
begin
re;
main;
end.
T2 $ water $
基本思路
这题是一道十分奇妙的图论题。首先考虑一个点的积水深度如何计算,假设这个点开始时有无限的水深,这些多出来的水肯定会沿着一某些路径流走,随着水深不断下降,显然当水深到达所有路径中最大点高度的最小值时,余下的水就无法流走了,这时的水深就是答案。
现在的问题就是如何找出这个最小值。要求出最小值,首先要找到这条路径,所有点的路径是一棵瓶颈生成树。
瓶颈生成树:无向图 $ G $ 的一颗瓶颈生成树是这样的一颗生成树,它最大的边权值在G的所有生成树中是最小的。
无向图 $ G $ 的最小生成树一定是瓶颈生成树(逆命题不成立),因为如果最小生成树不是瓶颈生成树,则最小生成树中一定有一条边可以被更换为更小的权值,不满足最小生成树的性质。
对于此题,将一个点与其上下左右四个点之间连边,边权为两个顶点高度的较大值。对于四周的点,还要将它们向一个 $ 0 $ 号节点连边,边权为 $ 0 $ 和点的高度中的较大值。之后在建成的图上求出最小生成树,就可以求出水流的所有路径。
之后以 $ 0 $ 节点为生成树的根,通过 $ DFS $ 处理答案,其中 $ i $ 节点的水深为从 $ i $ 到根的路径上,边权的最大值减去节点 $ i $ 的高度。
程序
program water;
const x:array[1..4]of longint=(1,0,-1,0);
y:array[1..4]of longint=(0,1,0,-1);
type tp=record
x,y,w:longint;
end;
type tp2=record
l,v,w:longint;
end;
var
map,ans:array[0..305,0..305]of longint;
fa,r,maxx:array[0..1000005]of longint;
e:array[0..200005]of tp2;
a:array[0..500005]of tp;
n,m,tot,i:longint;
function max(a,b:longint):longint;
begin
if a>b then exit(a)
else exit(b);
end;
procedure build(x,y,z:longint);
begin
inc(i);
e[i].l:=r[x];
r[x]:=i;
e[i].v:=y;
e[i].w:=z;
inc(i);
e[i].l:=r[y];
r[y]:=i;
e[i].v:=x;
e[i].w:=z;
end;
procedure qsort(l,r:longint);
var i,j,mid:longint;
t:tp;
begin
i:=l;
j:=r;
mid:=a[(i+j) shr 1].w;
repeat
while a[i].w<mid do inc(i);
while a[j].w>mid do dec(j);
if i<=j then begin
t:=a[i];
a[i]:=a[j];
a[j]:=t;
inc(i);
dec(j);
end;
until i>j;
if i<r then qsort(i,r);
if l<j then qsort(l,j);
end;
function find(q:longint):longint;
begin
if fa[q]=q then exit(q);
fa[q]:=find(fa[q]);
exit(fa[q]);
end;
procedure re;
var i,j:longint;
begin
for i:=1 to n do
for j:=1 to m do
read(map[i,j]);
end;
procedure dfs(f,fa:longint);
var i,x,y:longint;
begin
x:=(f-1) div m;
y:=f-x*m;
ans[x,y]:=maxx[f]-map[x,y];
i:=r[f];
while i<>0 do begin
if e[i].v<>fa then begin
maxx[e[i].v]:=max(maxx[f],max(maxx[e[i].v],e[i].w));
dfs(e[i].v,f);
end;
i:=e[i].l;
end;
end;
procedure main;
var i,j,k:longint;
begin
for i:=1 to n do
for j:=1 to m do
for k:=1 to 4 do begin
inc(tot);
a[tot].x:=(i)*m+j;
a[tot].y:=(i+x[k])*m+j+y[k];
if (i+x[k]<1) or (j+y[k]<1) or (i+x[k]>n) or (j+y[k]>m) then a[tot].y:=1;
a[tot].w:=max(map[i,j],map[i+x[k],j+y[k]]);
end;
qsort(1,tot);
for i:=1 to 2*n*m do begin
maxx[i]:=-maxint;
fa[i]:=i;
end;
for i:=1 to tot do begin
if find(a[i].x)<>find(a[i].y) then begin
fa[find(a[i].x)]:=find(a[i].y);
build(a[i].x,a[i].y,a[i].w);
end;
end;
dfs(1,1);
for i:=1 to n do begin
for j:=1 to m do
write(ans[i,j],' ');
writeln;
end;
end;
begin
read(n,m);
re;
main;
end.
T3 $ gcd $
基本思路
咕