LOJ 6131 Fiend - 行列式 - 可并堆 - 贪心

题目大意:给定n个区间,求: ∑ p ( − 1 ) f ( p ) ∏ i = 1 n [ p i ∈ [ L i , R i ] ] ,   n ≤ 100000 \sum_{p}(-1)^{f(p)}\prod_{i=1}^n[p_i\in [L_i,R_i]],\ n\le100000 p(1)f(p)i=1n[pi[Li,Ri]], n100000(多组数据XD)
其中p为排列,f§表示p中逆序对数量。
题解:看到这个形式很想行列式,事实上,就是一个矩阵,满足第i行的[Li,Ri]是1,其余是0,的行列式。
直接求是三方的,就saygoodbye了。注意到每一行只有一段连续的1,而我们最后想把他消成只有对角线有值的情况。
然后发现,例如,第一列是1的那些行拿出来,选出一个,用其他的减去这一个;
而选啥是随意的,因此显然选一个右端点最靠左的,这样能保证被减去后还以一行连续的1(或者直接一行都是0),也就是可以维护这个题目的性质(否则破坏了性质还做什么)。这样相当于是这些被减去的行会被放到后面考虑。
这个过程直接维护无论怎么办好像都复杂度不够优秀,因此我们需要一个能够整体把一个东西并到另一个东西上,并且能够取出最小值的数据结构,咦,这个好像就是可并堆啊……因此上左偏树。实现的时候顺便求一下每个右端点是在哪一行方便统计行列式的系数。(其实也就顺便发现这个行列式的权值一定是1,0,-1)如果到某个位置发现堆为空,那么行列式是0;以及每到一个位置先把右端点<当前枚举列的位置的点删去。
代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 200010
#define gc getchar()
using namespace std;
int node_cnt,val[N],id[N],p[N],lef[N],rig[N],dis[N];
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
inline int new_node(int v,int d)
{
    int x=++node_cnt;dis[x]=0;
    val[x]=v,p[id[x]=d]=x;
    return lef[x]=rig[x]=0,x;
}
int merge_lst(int x,int y)
{
    if(!x||!y) return x+y;
    if(val[x]>val[y]) swap(x,y);
    rig[x]=merge_lst(rig[x],y);
    if(dis[rig[x]]>dis[lef[x]]) swap(lef[x],rig[x]);
    if(rig[x]) dis[x]=dis[rig[x]]+1;else dis[x]=0;
    return x;
}
struct lst{
    int rt;
    inline int init() { return rt=0; }
    inline int merge(const lst &t) { return rt=merge_lst(rt,t.rt); }
    inline int pop() { return rt=merge_lst(lef[rt],rig[rt]); }
    inline int topv() { return val[rt]; }
    inline int topid() { return id[rt]; }
    inline int empty() { return !rt; }
    inline int insert(int v,int id) { return rt=merge_lst(rt,new_node(v,id)); }
}t[N];
int main()
{
    for(int T=inn(),n;T;T--)
    {
        node_cnt=0,n=inn();
        for(int i=1;i<=n;i++) t[i].init();
        for(int i=1,l,r;i<=n;i++)
            l=inn(),r=inn(),t[l].insert(r,i);
        int ans=1;
        for(int i=1,v,d;i<=n;i++)
        {
			while(!t[i].empty()&&t[i].topv()<i) t[i].pop();
            if(t[i].empty()) { ans=0;break; }
            v=t[i].topv(),d=t[i].topid(),t[i].pop();
            t[v+1].merge(t[i]);
            if(d^i) ans*=-1,p[id[p[i]]=d]=p[i];
        }
        printf("%c\n","FDY"[ans+1]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值