题目链接: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;
}