其实这只是一次改题的经过而已….
Problem1:分割(divide)
小N想要模拟出小T家后面的山水景观,于是他找了个N*M的凹槽,某些地方放入了石块作为山,然后在剩下的部分灌入水。比较凑巧的是剩下的部分正好形成了 1个连通块。
小A向小N提议在某些地方放入石块使得水被分成超过1个的连通块,小N觉得主意不错,于是准备去外面再去找些石块,因为石块比较难找,所以小N想知道最少还需要多少个石块才能实现。如果怎么样都不能分成超过1个的连通块的话就输出-1。水是4连通的.
5 5
#####
#. . .#
#####
#. . .#
#####
100%数据N,M<=50
思路:
其实这道题表面上看起来好像很难,其实是一道比较简单的题。首先分析样例,多画几次图会发现其实分成联通块的地方是一个角落或者是只有一格水,两边是石头,这两种情况,而且答案只能是-1,1,2。所以我们可以做几次dfs去求联通,最后再特判答案为2的情况。
Problem2:小A的作业(city)
小A的老师给小A布置了这样一份作业:某个城市x是“重要”的,当且仅当存在两个点a,b(a<>x,b<>x),当x不能通过时,a->b的最短路径的值改变了,现在告诉你N 个城市和M条连接城市之间的路径,求出哪些点是重要的。小A忙着去找小N所以没空做作业。请帮助小A算出哪些城市是重要的。如果不存在就输出"No important cities."给出的是一个无向图。
60%数据中N<=100 100%数据中N<=200,M<=N*N,0<=c<=10000
思路:
其实这道题的本质是问你从a点到b点的最短路有没有多条路,这样就可以直接Floyd或者是dijkstra+堆优化。d[j]>d[i]+e[i,j] 则记录下j的必经点中为i,若d[i]+e[i,j]=d[j],则说明有多个路径到j,再记录一下即可。本题没有什么特别的技巧。
附代码
var
a,v,g:array[1..500,0..500] of longint;p:boolean;
i,n,m,x,y,z,tot,j:longint;
dis:array[1..500] of longint;
bz,w:array[1..500] of boolean;
h:array[0..10000,1..2] of longint;
procedure swap(x,y:longint);
begin h[0]:=h[x];h[x]:=h[y];h[y]:=h[0];end;
procedure delete;
var i,j:longint;
begin
h[1]:=h[tot];dec(tot);
i:=1;j:=2;
while j<=tot do begin
if (j<tot)and(h[j,2]>h[j+1,2]) then inc(j);
if h[j,2]<h[i,2] then begin
swap(i,j);
i:=j;j:=j*2;
end else break;
end;
end;
procedure insert(x,y:longint);
var i,j:longint;
begin
inc(tot);h[tot,1]:=x;h[tot,2]:=y;
i:=tot;j:=tot div 2;
while j>0 do begin
if h[i,2]<h[j,2] then begin
swap(i,j);
i:=j;j:=j div 2;
end else break;
end;
end;
procedure dijkstra(x:longint);
var i,now:longint;
begin
fillchar(bz,sizeof(bz),false);
fillchar(dis,sizeof(dis),$7f);
tot:=0;insert(x,0);dis[x]:=0;
while true do begin
while (tot>0)and(bz[h[1,1]]) do
delete;
if tot=0 then break;
now:=h[1,1];
bz[now]:=true;
for i:=1 to a[now,0] do
if dis[a[now,i]]>dis[now]+v[now,a[now,i]] then begin
dis[a[now,i]]:=dis[now]+v[now,a[now,i]];
insert(a[now,i],dis[a[now,i]]);g[x,a[now,i]]:=now;end
else if dis[a[now,i]]=dis[now]+v[now,a[now,i]] then g[x,a[now,i]]:=0;
end;
end;
begin
assign(input,'city.in');reset(input);
assign(output,'city.out');rewrite(output);
readln(n,m);
for i:=1 to m do begin
read(x,y,z);
if x=y then continue;
if (v[x,y]=0) then begin
inc(a[x,0]);a[x,a[x,0]]:=y;
inc(a[y,0]);a[y,a[y,0]]:=x;
v[x,y]:=z;v[y,x]:=z;
end else if (v[x,y]>z) then begin
v[x,y]:=z;v[y,x]:=z;
end;
end;
for i:=1 to n do begin
dijkstra(i);
for j:=1 to n do
if g[i,j]<>0 then if (g[i,j]<>i)and(g[i,j]<>j) then w[g[i,j]]:=true;
end;
for i:=1 to n do if w[i] then begin write(i,' ');p:=true;end;
if not p then writeln('No important cities.');
close(input);close(output);
end.
另外Floyd:
for u:=1 to n do
for i:=1 to n do
if (f[u,i]<>max) then
for j:=1 to n do
if (i<>j)and(f[u,j]<>max) then
begin
if f[u,i]+f[u,j]<f[i,j] then
begin
f[i,j]:=f[u,i]+f[u,j];f[j,i]:=f[i,j];
use[i,j]:=u;use[j,i]:=u;
end else
if f[u,i]+f[u,j]=f[i,j] then
use[i,j]:=0;
end;
但是还是要注意dijkstra的堆打法,易错。
再还有spfa+lll+slf优化:
fillchar(dis,sizeof(dis),$7f);
l:=0;t[1]:=n+1;t[2]:=n+1+n+2;r:=2;dis[n+1]:=0;dis[n+1+n+2]:=0;cont:=2;su:=0;
while l<>r do begin
l:=l mod mo+1;
while dis[t[l]]>su div cont do begin
r:=r mod mo+1;t[r]:=t[l];l:=l mod mo+1;
end;
no:=t[l];
p:=last[no];
dec(cont);dec(su,dis[t[l]]);
while p<>0 do begin
if (dis[no]+v[p]<dis[bi[p]])or(dis[bi[p]]=dis[0]) then begin
dis[bi[p]]:=dis[no]+v[p];
if not bz[bi[p]] then begin
bz[bi[p]]:=true;inc(cont);su:=su+dis[bi[p]];
if dis[bi[p]]<dis[t[l mod mo+1]] then begin
t[l]:=bi[p];l:=(l+mo-2)mod mo+1;
end else begin
r:=r mod mo+1;t[r]:=bi[p];
end;
end;
end;
p:=next[p];
end;
bz[no]:=false;
end;
Problem3:连续段的期望(nine)
小N最近学习了位运算,她发现2个数xor之后数的大小可能变大也可能变小,and之后都不会变大,or之后不会变小。于是她想算出以下的期望值,现在有 N个数排成一排,如果她随意选择一对l,r并将下标在l和r中间(包括l,r)的数(xor,and,or)之后期望得到的值是多少呢?取出每一对l,r 的概率都是相等的。
2
4 5
2.750 4.250 4.750
每一组l,r取的概率都是相同的,xor=(4+1+1+5)/4=2.750 其他同理(正反即可如(l,r)和(r,l))
思路:
见到位运算,就把它们转成二进制来看,然后你会发现只用一位位来做(每次可以加i个序列,及当做到第j位第i个数时,那么可以和前面的1~i组合,一共有i个),做32次,每次处理每一位的and,xor,or的和即可。
or处理:因为只要有一个数的一位为1,那么从它前面的任意包括他自己k到i or出来的结果都是1,那么答案就可以加2^j*2了。在算的过程中求出最近的1所在的第k个数的第j位,答案就是k*2^j*2。(乘2是因为可以反过来)
其他的类似。
代码:
for k:=1 to 32 do begin orp:=0;orn:=0;ao:=0;ad:=0;ax1:=0;ax0:=0;ax:=0;
for i:=1 to n do begin
if a[i,k]=1 then orp:=i;
if a[i,k]=0 then orn:=i;
if orp<>0 then ao:=ao+orp;
if (a[i,k]=1) then ad:=ad+i-orn;
if a[i,k]=1 then begin t:=ax0;ax0:=ax1;ax1:=t+1;end else ax0:=ax0+1;
ax:=ax+ax1;
if a[i,k]=1 then begin dec(ao);dec(ad);dec(ax);end;
end;
bo:=bo+ao*mi*2;bd:=bd+ad*mi*2;bx:=bx+ax*mi*2;mi:=mi*2;
end;
writeln(bx/(n*n):0:3,' ',bd/(n*n):0:3,' ',bo/(n*n):0:3);
总结:
总而言之,这次做的模拟赛,其实收获很大
1.虽然是当练习,但是反映了我的思维不够灵活,如第二题的转换。
2.且当题目看起来比较难时,要多找找例子,说不定就是一道水题。
3.位运算要多往转成二进制后和and,or,xor自身的特点去做。