Fiend

题目大意

满足li<=pi<=ri的排列p,问逆序对是奇数的多还是逆序对是偶数的多或者是否一样多?

解法

可以构造一个矩阵,ai,li~ri是1,然后就是问它行列式的符号或判断行列式是否为0。
模拟高斯消元,每次消到第i行时,让第i行变成第i列上有1且r最小的,这样可以保证任意时刻1都是一段区间。
可以用可并堆、平衡树或线段树来快速支持合并。

#include<cstdio>
#include<algorithm>
#include<set>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10,maxtot=maxn*100;
struct dong{
    int x,y;
    friend bool operator <(dong a,dong b){
        return a.y<b.y||a.y==b.y&&a.x<b.x;
    }
} a[maxn];
int L[maxn],R[maxn],rank[maxn],sa[maxn];
set<dong> s[maxn];
int root[maxn],tree[maxtot],left[maxtot],right[maxtot];
int fa[maxn];
int i,j,k,l,t,n,m,ca,tot,top;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int getfa(int x){
    return fa[x]?fa[x]=getfa(fa[x]):x;
}
void change(int &x,int l,int r,int a,int b){
    if (!x) x=++tot;
    tree[x]+=b;
    if (l==r) return;
    int mid=(l+r)/2;
    if (a<=mid) change(left[x],l,mid,a,b);else change(right[x],mid+1,r,a,b);
}
int query(int x,int l,int r){
    if (l==r) return l;
    int mid=(l+r)/2;
    if (tree[left[x]]) return query(left[x],l,mid);else return query(right[x],mid+1,r);
}
int merge(int l,int r,int a,int b){
    if (!a||!b) return a+b;
    if (l==r){
        tree[a]+=tree[b];
        return a;
    }
    int mid=(l+r)/2;
    left[a]=merge(l,mid,left[a],left[b]);
    right[a]=merge(mid+1,r,right[a],right[b]);
    tree[a]=tree[left[a]]+tree[right[a]];
    return a;
}
void swa(int x,int y){
    int k=getfa(L[x]);
    change(root[k],1,n,rank[x],-1);
    swap(L[x],L[y]);
    swap(R[x],R[y]);
    swap(rank[x],rank[y]);
    swap(sa[rank[x]],sa[rank[y]]);
    k=getfa(L[y]);
    change(root[k],1,n,rank[y],1);
}
void move(int x,int y){
    int a=getfa(x),b=getfa(y);
    root[b]=merge(1,n,root[a],root[b]);
    fa[a]=b;
}
int work(){
    int t=1;
    bool gjx;
    fo(i,1,n) root[i]=fa[i]=0;
    fo(i,1,tot) tree[i]=left[i]=right[i]=0;
    tot=0;
    fo(i,1,n){
        change(root[L[i]],1,n,rank[i],1);
    }
    fo(i,1,n){
        gjx=0;
        k=getfa(i);
        l=tree[root[k]];
        while (l--){
            j=query(root[k],1,n);
            change(root[k],1,n,j,-1);
            if (R[sa[j]]>=i){
                gjx=1;
                break;
            }
        }
        if (!gjx) return 0;
        j=sa[j];
        if (j!=i){
            t=-t;
            swa(i,j);
        }
        move(i,R[i]+1);
    }
    return t;
}
int main(){
    freopen("fiend.in","r",stdin);freopen("fiend.out","w",stdout);
    ca=read();
    while (ca--){
        n=read();
        fo(i,1,n) L[i]=read(),R[i]=read();
        fo(i,1,n) a[i].x=i,a[i].y=R[i];
        sort(a+1,a+n+1);
        fo(i,1,n) rank[a[i].x]=i,sa[i]=a[i].x;
        t=work();
        if (t==0) printf("D\n");
        else if (t>0) printf("Y\n");
        else printf("F\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值