洛谷3460 [POI2007]TET-Tetris Attack(贪心)(树状数组)

题意

给定一个长度为2n的序列,1~n各出现两次,可以交换相邻两项,两个同样的数放在一起会对消,求把所有数对消的最小交换次数,并输出方案。

特性

一种很显然的贪心,两两间原始距离最近的先合并,再到远的。这么做是为了在包含关系的时候先处理掉里面的。
如果要是交叉的话,先删哪个都没所谓,可以自己YY一下。

心路

所以我就想排序后用树状数组维护编号,其它模拟就好,因为总步数才1 000 000,用并查集跳过删除的数字。
结果又T又W(大部分还是对的),不知道为什么。

题解

贪心+树状数组
其实排序可以省掉,只要按顺序每到两个都出现的时候移动到它们消掉就可以了。因为按顺序的时候肯定先把区间小的先访问到,这样省了个O(N logN)的时间。
树状数组不变,还有,因为在把一个数字移到相对应的数字旁边时,很明显路径是连续的,所以可以用一个for来填写。

代码

#include
#include
#include
using namespace std;
const int MAXN=50010,MAXC=1000010;//debug 看错 MAXC!=2*MAXN

int n,m,size=0,pos[MAXN];

int s[MAXN*2];
void add(int x,int c)
{
    for(;x<=m;x+=x&-x) s[x]+=c;
}
int getsum(int x)
{
    int re=0;
    for(;x>=1;x-=x&-x) re+=s[x];

    return re;
}

int tot=0,list[MAXC];
int main()
{
    scanf("%d",&n);m=2*n;
    for(int i=1;i<=m;i++)
    {
        int x;scanf("%d",&x);
        if(!pos[x]) pos[x]=i,add(i,1);
        else
        {
            add(pos[x],-1);int res=getsum(i)-getsum(pos[x]-1);//求出两点之间非空的位置有多少个
            for(int p=i-size-1;res;res--,p--) list[++tot]=p;//填写方案数组
            size+=2;
        }
    }
    
    printf("%d\n",tot);
    for(int i=1;i<=tot;i++) printf("%d\n",list[i]);
    return 0;
}

 

你可能感兴趣的:(树状数组,刷题之路,贪心)