SDOJ #2013 隨機數生成器(笛卡爾樹)

題目鏈接:SDOJ #2013

題目大意:給出一個n*m(n,m<=5000)的網格,每個網格有互不相同的權值,定義兩個格子聯通當且僅當從其中的某個格子只向下或向右走能到達另一個格子,求網格中字典序最大的反鏈。

題解:反鏈即不存在一個被選中的格子位於另一個被選中的格子的右下方,要求字典序最大即每次選可選的格子中權值最大的一個。關鍵在於如何快速找到可選的權值最大的格子。
由於每次被標記不合法的格子會是左上或右下的一整塊,每一行可選的格子是連續的一段。所以可以對每一行建一棵笛卡爾樹,查詢最大值時比較每一行笛卡爾樹根的權值即可。笛卡爾樹也可以方便地實現前後區間段的刪除。

第一次寫笛卡爾樹,感覺比較神奇(*^▽^*),維護一個單調棧可以 O(n) 把一個序列提成笛卡爾樹,詳見代碼。

code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
inline int read()
{
    char c=getchar(); int num=0,f=1;
    while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
    while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
    return num*f;
}
inline LL readl()
{
    char c=getchar(); LL num=0,f=1;
    while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
    while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
    return num*f;
}
int n,m,k,top,ans[5005][2],sta[5005],fa[5005][5005],ch[5005][5005][2],rt[5005],L[5005],R[5005];
LL P0,P1,Q0,Q1,A,B,C,D,A1,B1,C1,D1;
double a[5005][5005];
int main()
{
    n=read(); m=read(); k=read();
    for (int i=1;i<=n;i++)
    {
        A=readl(); B=readl(); C=readl(); D=readl(); P0=readl();
        A1=readl(); B1=readl(); C1=readl(); D1=readl(); Q0=readl();
        a[i][0]=(double)P0/(double)(Q0+1);
        for (int j=1;j<=m;j++)
        {
            P1=(A*P0*P0+B*P0+C)%D;
            Q1=(A1*Q0*Q0+B1*Q0+C1)%D1;
            a[i][j]=(double)P1/(double)(Q1+1);
            swap(Q0,Q1); swap(P0,P1);
        }
    }
    for (int i=1;i<=k;i++)
    {
        int x=read(),y=read(),z=read(),w=read();
        swap(a[x][y],a[z][w]);
    }
    for (int i=1;i<=n;i++)  //對每一行建笛卡爾樹
    {
        top=0;
        for (int j=1;j<=m;j++) 
        {
            int tmp=0;
            while (top&&a[i][j]>a[i][sta[top]])
            {
                ch[i][sta[top]][1]=tmp; fa[i][tmp]=sta[top]; tmp=sta[top];
                ch[i][j][0]=sta[top]; fa[i][sta[top]]=j; top--;
            }
            if (top) ch[i][sta[top]][1]=j,fa[i][j]=sta[top];
            sta[++top]=j;
        }
        rt[i]=sta[1]; L[i]=1; R[i]=m;
    }
    top=0;
    while (1)
    {
        double cur=0; int now=0;
        for (int i=1;i<=n;i++) if (rt[i]&&a[i][rt[i]]>cur) cur=a[i][rt[i]],now=i;
        if (!now) break; ans[++top][0]=now; ans[top][1]=rt[now];
        for (int i=now-1;i>=1;i--) if (rt[i]&&L[i]<=rt[now])   //刪除也很方便
        {
            for (int j=L[i];j<=rt[now];j++) if (rt[i])
            {
                if (fa[i][j]) ch[i][fa[i][j]][0]=ch[i][j][1],fa[i][ch[i][j][1]]=fa[i][j];
                 else rt[i]=ch[i][j][1],fa[i][ch[i][j][1]]=0;
            }
            L[i]=rt[now]+1;
        }
        for (int i=now+1;i<=n;i++) if (rt[i]&&rt[now]<=R[i])
        {
            for (int j=R[i];j>=rt[now];j--) if (rt[i])
            {
                if (fa[i][j]) ch[i][fa[i][j]][1]=ch[i][j][0],fa[i][ch[i][j][0]]=fa[i][j];
                 else rt[i]=ch[i][j][0],fa[i][ch[i][j][0]]=0;
            }
            R[i]=rt[now]-1;
        }
        rt[now]=0;
    }
    printf("%d\n",top);
    for (int i=1;i<=top;i++) printf("%d %d\n",ans[i][0],ans[i][1]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章