zoukankan      html  css  js  c++  java
  • 【安徽集训】fiend

    • 考试的时候只会 (O(Tn^3)) 的做法,其它题还都不会,想到一整场就打这么点是人都能写的暴力没啥意思,就懒得写了。。

    Description

      双人博弈。每一轮 A 和 B 同时选择一个 (1 ext{~} n) 的排列 (P_i),必须满足 (L_ile P_ile R_i)。同时,A 选择的排列的逆序对数必须是偶数,B 选择的排列的逆序对数必须是奇数(显然 A 的排列和 B 的排列不可能相同)。每一轮选择的排列不能和之前出现过的排列相同。先无法选择排列的输,若双方同时无法选择排列则和局。
      (T) 组数据,每次给定 (n,L_i,R_i),求游戏的结果。

    Solution

      就是让你比逆序对数是奇数的排列多 还是是偶数的排列多。

      前置知识:行列式
      看到逆序对计数,我们很容易想到行列式。
      因为行列式的公式是 $$det(a) = sumlimits_{p_{1cdots n}} (-1)^{sigma(p)} prodlimits_{i=1}^n a_{i,p_i}$$   其中 (p)(1)(n) 的任意排列,(sigma(p)) 表示排列 (p) 的逆序对数。
      这个公式本身就和逆序对数有关系。
      其中排列 (p) 等价于本题中 A 和 B 选择的排列。
      然后关键就是后面那个 (prodlimits_{i=1}^n a_{i,p_i}) 的意义。
      显然,只要有一个 (a_{i,p_i})(0),这个式子就为 (0)
      那么不难想到,对于一组限制 (L_ile P_ile R_i),可以将矩阵的第 (i) 行的第 (L_i)(R_i) 位设为 (1),其余位设为 (0)
      这样 (prodlimits_{i=1}^n a_{i,p_i} = 1) 当且仅当排列 (p) 中的每一个数 (p_i) 都满足限制 (L_ile p_ile R_i)。其余情况下 (prodlimits_{i=1}^n a_{i,p_i} = 0),表示不满足限制。
      现在只有所有满足限制的排列 (p) 才会被计算一次,下面考虑 (sumlimits_{p_{1cdots n}} (-1)^{sigma(p)})
      这就是把逆序对数为奇数的排列 (p) 的权值记为 (-1),逆序对数为偶数的排列 (p) 的权值记为 (1)
      所以最后算出来的行列式的值 (det(a) = 满足条件的逆序对数为偶数的排列数 - 满足条件的逆序对数为奇数的排列数)
      直接判断 (det(a))(gt 0)(lt 0) 还是 (=0) 就好了。

      那行列式 (det(a)) 的值怎么算?
      最暴力的是 (O(n^3)) 的高斯消元做法,即把行列式高消成上三角,对角线上所有数的乘积 就是行列式的值。
      因为有 (100) 组数据,这个做法只能得 (35) 分,但在高消时判断若 (a_{i,i}=0) 则直接返回行列式值为 (0),可以卡到 (70) 分。

      观察到矩阵中所有数都是 (0,1),而且每行只有一个连续段是 (1),其余位置都是 (0)。那么可以优化高消过程。
      依然从小到大枚举 (x),找到左端点为 (x) 的右端点最小的区间,设其右端点为 (y),则模拟高消过程,其余左端点为 (x) 的区间的左端点都会被挪到 (y+1)
      把所有全 (1) 连续段按左端点分类,那么这一轮高消就是把左端点为 (x) 的集合 合并到左端点为 (y+1) 的集合。
      同时我们还要支持查询一个集合中的最小值(集合中存储所有连续段的右端点)。
      左偏树即可。
      复杂度 (O(Tnlog n))

    #include<bits/stdc++.h>
    #define N 100002
    using namespace std;
    inline int read(){
        int x=0; bool f=1; char c=getchar();
        for(;!isdigit(c); c=getchar()) if(c=='-') f=0;
        for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
        if(f) return x;
        return 0-x;
    }
    int n,l[N],r[N],siz,rt[N],ch[N][2],dis[N],lst[N];
    bool vis[N];
    int merge(int x, int y){
        if(!x || !y) return x|y;
        if(r[x]>r[y]) swap(x,y);
        ch[x][1] = merge(ch[x][1],y);
        if(dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]);
        dis[x] = dis[ch[x][1]]+1;
        return x;
    }
    inline void ins(int L, int x){
    	rt[L] = merge(rt[L], x);
    }
    inline int popNode(int x){
    	return merge(ch[x][0], ch[x][1]);
    }
    int solve(){
        for(int i=1; i<=n; ++i) rt[i]=ch[i][0]=ch[i][1]=dis[i]=0, vis[i]=0;
        
    	for(int i=1; i<=n; ++i) ins(l[i],i);
        int ans=1;
        for(int i=1; i<=n; ++i){
            int x=rt[i];
            if(!x) return 0;
            rt[i] = popNode(x);
            lst[i] = x;
            if(rt[i] && r[rt[i]]==r[x]) return 0;
            rt[r[x]+1] = merge(rt[i], rt[r[x]+1]);
        }
        if(ans==0) return 0;
        
        int tot = n;
        for(int i=1; i<=n; ++i) if(!vis[i]){
        	--tot; int x=i;
        	do{
        		vis[x]=1, x=lst[x];
        	}while(x!=i);
        }
        if(tot&1) ans=-ans;
        return ans;
    }
    int main(){
        int T=read();
        while(T--){
            n=read();
            for(int i=1; i<=n; ++i) l[i]=read(), r[i]=read();
            int ans=solve();
            if(ans==1) puts("Y");
            else if(ans==-1) puts("F");
            else puts("D");
        }
        return 0;
    }
    /*
    3
    1
    1 1
    2
    2 2
    1 1
    2
    1 2
    1 2
    */
    
  • 相关阅读:
    GAMBIT、ICEM、HYPERMESH耦合面的处理方法
    如何用hypermesh生成包含interface的流体网格
    python求数字位数的方法
    drawPython
    Python入门计划
    书法与篆刻创作结课
    易忽视的Python知识点
    Ubuntu中,wxpython的TextCtrl引发的error:_pixman_log_error
    在Ubuntu15.10中,使用wxPython的webview和JS进行交互
    PyCharm导入tensorflow包
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/11608142.html
Copyright © 2011-2022 走看看