http://www.tsinsen.com/A1363
给一个 n×n 大小的、每个格子具有高度的棋盘(姑且看作是个沙盘模型吧)灌水,求这个棋盘灌水后的状态种数。
这个题实在太神了,我跪了半个上午+一个下午才AC,跪跪跪
首先我们可以想到,可能由于某些格子组成的“墙”的阻挡,水被分成了若干个小水池,这些水池之间是互不影响的,一个水池水位高一点、低一点不会另一个水池造成任何影响,除非水位上涨漫过了那些墙,这样几个水池就会连在一起。
那么我们可以模拟水位上涨的过程,假设最开始水位高度为最高的那个格子的高度,并且水从最高的那个格子像泉水一样源源不断地流出来。先对 n2 个格子按照第一关键字高度,第二关键字x坐标,第三关键字y坐标(均为升序)排序,然后从小到大来遍历每个格子,对于每个格子 x 而言,如果其相邻的格子 x′ 比它高度低,并且两个格子不在同一个联通块,那么就合并两个格子所在的联通块,相当于模拟水漫过了格子,两个水池合并成一个水池。其间 x′ 的水位的方案数就增加 |hx−hx′| ,表示 x′ 的水位增加的幅度在 [1,hx−hx′] 内,两个水池是不会合并的,仍然是同一状态。合并以后,新的水池的水位方案数是之前两个水池的水位方案数的乘积(乘法计数)。
后记
其实这个题目思路并不难,然而丧病的是这个题需要写个高精度,而且最终答案的数字特别大,这意味着需要写个压位,然后我因为高精度刚开始没压位,一直没发现为什么WA,然后补上压位( 107 压位)后,没有注意到乘法过程中会爆掉int,高精度中每一位的保存方式改成long long int后,我把别人以前AC掉的pascal标称交上去TLE 2发,一直不知道怎么回事,然后我猜想可能是高精度的长度开小了,果真如此(天杀的hwd改数据了!!!)。然后我也调整了高精度的数字长度范围,交上去RE 2发,发现可能是MLE了(天杀的THUSC卡内存!!!),没办法,只好试图继续调整范围,最后以244MB的内存卡过了此题。感觉自己真的好弱啊,居然会如此无脑地WA+TLE+RE 11发才AC这题
感谢http://www.cnblogs.com/Randolph87/p/3769796.html提供此题的pascal标程供我对拍查错
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 103
#define LEN 3000
#define calc(i,j) (((i)-1)*n+(j))
#define BASE 10000000
using namespace std;
typedef long long int LL;
int n,m;
struct Hugeint
{
LL num[LEN];
int len;
void print()
{
printf("%lld",num[len]);
for(int i=len-1;i>=1;i--) printf("%07lld",num[i]);
}
}ans[MAXN*MAXN],a,b,t;
struct Point
{
int x,y,num;
}points[MAXN*MAXN];
bool inMap(int x,int y)
{
if(x<1||x>n||y<1||y>n) return false;
return true;
}
Hugeint operator+(Hugeint a,LL b)
{
a.num[1]+=b;
for(int i=1;i<=a.len;i++)
{
a.num[i+1]+=a.num[i]/BASE;
a.num[i]%=BASE;
}
while(a.num[a.len+1])
{
a.len++;
a.num[a.len+1]+=a.num[a.len]/BASE;
a.num[a.len]%=BASE;
}
return a;
}
Hugeint operator*(Hugeint a,Hugeint b)
{
Hugeint c;
memset(c.num,0,sizeof(c.num));
c.len=a.len+b.len-1;
for(int i=1;i<=a.len;i++)
for(int j=1;j<=b.len;j++)
c.num[i+j-1]+=a.num[i]*b.num[j];
for(int i=1;i<=c.len-1;i++)
{
c.num[i+1]+=c.num[i]/BASE;
c.num[i]%=BASE;
}
while(c.num[c.len]>=BASE)
{
c.num[c.len+1]=c.num[c.len]/BASE;
c.num[c.len]%=BASE;
c.len++;
}
return c;
}
int map[MAXN][MAXN];
int f[MAXN*MAXN];
int h[MAXN*MAXN]; //h[i]=联通块i的水位高度
int findSet(int x)
{
if(f[x]==x) return f[x];
return f[x]=findSet(f[x]);
}
int xx[]={1,-1,0,0},yy[]={0,0,1,-1};
bool cmp(Point a,Point b)
{
if(map[a.x][a.y]==map[b.x][b.y])
{
return a.num<b.num;
}
return map[a.x][a.y]<map[b.x][b.y];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
scanf("%d",&map[i][j]);
h[calc(i,j)]=map[i][j];
points[calc(i,j)].x=i,points[calc(i,j)].y=j,points[calc(i,j)].num=calc(i,j);
}
for(int i=1;i<=n*n;i++) //初始化第i号格子
{
f[i]=i;
ans[i].len=ans[i].num[1]=1;
}
sort(points+1,points+n*n+1,cmp); //!!!!!!
for(int i=1;i<=n*n;i++) //枚举第i号格子
for(int dir=0;dir<4;dir++)
{
int nowx=points[i].x,nowy=points[i].y;
int nextx=nowx+xx[dir],nexty=nowy+yy[dir];
if(!inMap(nextx,nexty)) continue; //!!!!!!!
int roota=findSet(calc(nowx,nowy)),rootb=findSet(calc(nextx,nexty)); //roota是(nowx,nowy)所属联通块,rootb是(nextx,nexty)所属联通块
if(h[roota]>=h[rootb]&&roota!=rootb) //a联通块水位比b联通块水位高,而且它们并不是联通的
{
ans[rootb]=ans[rootb]+(h[roota]-h[rootb]); //那么b联通块的方案数要加上两个联通块之间的水位差
ans[roota]=ans[roota]*ans[rootb]; //两个联通块联通后,新联通块的方案数是两个老联通块方案数的乘积(乘法原理)
f[rootb]=roota; //并查集合并两个联通块
}
}
ans[findSet(1)]=ans[findSet(1)]+(m-map[points[n*n].x][points[n*n].y]);//所有的联通块都在一起,也就是整个地图上没有干旱的高地了,那么要加上剩余的方案(一直灌水到水位上升至m)
ans[findSet(1)].print();
printf("\n");
return 0;
}