題目鏈接: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;
}