[清橙A1363][THUSC 2012]水位(并查集+高精度压位)

来源:互联网 发布:55开淘宝店地址服装 编辑:程序博客网 时间:2024/05/23 01:33

题目链接

http://www.tsinsen.com/A1363

题目大意

给一个n×n大小的、每个格子具有高度的棋盘(姑且看作是个沙盘模型吧)灌水,求这个棋盘灌水后的状态种数。

思路

这个题实在太神了,我跪了半个上午+一个下午才AC,跪跪跪
首先我们可以想到,可能由于某些格子组成的“墙”的阻挡,水被分成了若干个小水池,这些水池之间是互不影响的,一个水池水位高一点、低一点不会另一个水池造成任何影响,除非水位上涨漫过了那些墙,这样几个水池就会连在一起。
那么我们可以模拟水位上涨的过程,假设最开始水位高度为最高的那个格子的高度,并且水从最高的那个格子像泉水一样源源不断地流出来。先对n2个格子按照第一关键字高度,第二关键字x坐标,第三关键字y坐标(均为升序)排序,然后从小到大来遍历每个格子,对于每个格子x而言,如果其相邻的格子x比它高度低,并且两个格子不在同一个联通块,那么就合并两个格子所在的联通块,相当于模拟水漫过了格子,两个水池合并成一个水池。其间x的水位的方案数就增加|hxhx|,表示x的水位增加的幅度在[1,hxhx]内,两个水池是不会合并的,仍然是同一状态。合并以后,新的水池的水位方案数是之前两个水池的水位方案数的乘积(乘法计数)。

后记
其实这个题目思路并不难,然而丧病的是这个题需要写个高精度,而且最终答案的数字特别大,这意味着需要写个压位,然后我因为高精度刚开始没压位,一直没发现为什么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 10000000using 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;}
0 0