[COCI2006-2007#3] LISTA
原题链接
首先,发现找不到操作序列可以利用的性质,我们直接按照题意写一个双向链表,把最后的序列处理出来。
然后发现实际上就是要求一个最短的插入排序操作序列。
容易想到求出这个序列的最长上升子序列,对于最长上升子序列中的数,我们无需操作。对于其以外的数,我们找到它需要插入的位置即可。
其最优性显然:
那些不需要操作的元素一定满足单调递增,剩下的元素一定需要操作。
实现细节
设最长上升子序列中的最大元素为
对于比
对于比 set 来找到后继并将其插入 set 即可。
时间复杂度
Code
#include<cstdio>
#include<set>
using namespace std;
const int SIZE=5e5+10;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x*10)+(ch^48),ch=getchar();
return x;
}
inline char readch(){char ch=getchar();while(ch<'A'||ch>'B')ch=getchar();return ch;}
inline int max(int x,int y){return x>y?x:y;}
inline int lowbit(int x){return x&-x;}
int n,k,v[SIZE],pre[SIZE],suf[SIZE],dp[SIZE],t[SIZE],maxdp,maxp,top;
bool vst[SIZE];
char type;
set<int>bin;
struct answer{char type;int x,y;}ans[SIZE];
inline void modify(int x,int val){while(x<SIZE)t[x]=max(t[x],val),x+=lowbit(x);}
inline int query(int x){int res=0;while(x)res=max(res,t[x]),x-=lowbit(x);return res;}
int main()
{
n=read(),k=read(),suf[0]=1,pre[n+1]=n;
for(int i=1;i<=n;i++)pre[i]=i-1,suf[i]=i+1;
for(int i=1,x,y,tmp;i<=k;i++)
{
type=readch(),x=read(),y=read();
if(type=='A')pre[suf[x]]=pre[x],suf[pre[x]]=suf[x],pre[x]=pre[y],suf[x]=y,suf[pre[y]]=x,pre[y]=x;
else pre[suf[x]]=pre[x],suf[pre[x]]=suf[x],pre[x]=y,suf[x]=suf[y],pre[suf[y]]=x,suf[y]=x;
}
for(int ths=suf[0],cnt=0;ths!=n+1;ths=suf[ths])v[++cnt]=ths;
for(int i=1;i<=n;i++)dp[i]=query(v[i])+1,modify(v[i],dp[i]);
for(int i=1;i<=n;i++)if(dp[i]>maxdp)maxdp=dp[i],maxp=i;
for(int i=maxp,ths=maxdp;i;i--)if(dp[i]==maxdp)vst[v[i]]=true,bin.insert(v[i]),maxdp--;
for(int i=v[maxp]-1;i;i--)if(!vst[i])ans[++top]={'A',i,*bin.lower_bound(i)},bin.insert(i);
for(int i=v[maxp]+1;i<=n;i++)ans[++top]={'B',i,i-1};
printf("%d\n",top);
for(int i=1;i<=top;i++)putchar(ans[i].type),putchar(' '),printf("%d %d\n",ans[i].x,ans[i].y);
return 0;
}