[JZOJ5199]Fiend

来源:互联网 发布:置乱 数据 算法 编辑:程序博客网 时间:2024/06/06 07:00

题目大意

给定n个限制,每个形如Li,Ri
你要生成n的排列{Pn},满足i,LiPiRi
请判断生成的排列中逆序对个数为奇数的排列多还是偶数的排列多。
一个测试点T组数据。

1T500,1n105,1n1.5×106,1LiRin


题目分析

题目中既有排列又有逆序对个数奇偶性,这启发我们从矩阵行列式入手。
考虑构造一个n阶矩阵,对于第i行,我们令LiRi列值为1,其余列值为0。那么我们求出这个矩阵的行列式,显然这个行列式的正负就能体现答案。
但是这题n很大,我们不能直接高斯消元。考虑利用矩阵的特殊性质来快速消元。
注意到题目中的矩阵每一行有值的都是一段区间(且值都是1),考虑在消元中有目的地选择行来使这个性质始终成立。
将限制挂在Li上,并且记录它的Ri以及原本是哪一行(i)。
然后我们从左到右消。假设我们考虑到位置x,我们从挂在x上面的所有限制中选择一个Ri值最小的,然后让它作为这一行的数。那么它就会和挂在这里的其它限制进行消元,因为选择的Ri是最小的,我们显然只需要把这些其它限制从x移动到Ri+1这个位置就好了。至于行列式在此次消元中是否变号,我们只需要看看选择的i原本是否就是第x行。
也就是说我们需要支持若干个集合的取最小值以及合并,使用左偏树就好了。
时间复杂度O(nlogn)


代码实现

#include <algorithm>#include <iostream>#include <cassert>#include <cstdio>#include <cctype>#include <queue>using namespace std;const int N=100050;int read(){    int x=0,f=1;    char ch=getchar();    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();    return x*f;}int idx[N],rel[N],root[N];int T,n;namespace leftist_tree{    int rank[N],key[N],cid[N];    int son[N][2];    queue<int> avl;    void init()    {        for (;!avl.empty();avl.pop());        for (int i=1;i<=n;++i) root[i]=0,avl.push(i);    }    int newnode(int val,int id)    {        int ret=avl.front();avl.pop();        return key[ret]=val,cid[ret]=id,son[ret][0]=son[ret][1]=0,rank[ret]=1,ret;    }    void release(int x){avl.push(x);}    int merge(int x,int y)    {        if (!x||!y) return x^y;        if (key[x]>key[y]) swap(x,y);        son[x][1]=merge(son[x][1],y);        if (rank[son[x][0]]<rank[son[x][1]]) swap(son[x][0],son[x][1]);        rank[x]=rank[son[x][1]]+1;        return x;    }    void push(int &rt,int val,int id){rt=merge(newnode(val,id),rt);}    void pop(int &rt,int &key_,int &id_){key_=key[rt],id_=cid[rt],release(rt),rt=merge(son[rt][0],son[rt][1]);}};int main(){    freopen("fiend.in","r",stdin),freopen("fiend.out","w",stdout);    for (T=read();T--;putchar('\n'))    {        n=read(),leftist_tree::init();        for (int i=1,l,r;i<=n;++i) idx[i]=rel[i]=i,l=read(),r=read(),leftist_tree::push(root[l],r,i);        int ret=1;        for (int i=1;ret&&i<=n;++i)        {            int key_,id_;            for (;root[i]&&leftist_tree::key[root[i]]<i;leftist_tree::pop(root[i],key_,id_));            if (!root[i]) ret=0;            else            {                leftist_tree::pop(root[i],key_,id_);                int tmp=idx[id_];                if (tmp!=i) ret*=-1,swap(idx[id_],idx[rel[i]]),swap(rel[i],rel[tmp]);                if (key_<n) root[key_+1]=leftist_tree::merge(root[i],root[key_+1]);            }        }        /*for (int i=1;i<=n;++i) cerr<<idx[i]<<' ';        cerr<<endl;        for (int i=1;i<=n;++i) cerr<<rel[i]<<' ';        cerr<<endl;        for (int i=1;i<=n;++i) assert(rel[idx[i]]==i);*/        if (ret>0) putchar('Y');        else if (!ret) putchar('D');        else putchar('F');    }    fclose(stdin),fclose(stdout);    return 0;}
原创粉丝点击