世界杯 - 非旋Treap

题目大意:
有n个人,每个人有一个k元组v。称A能胜B当且仅当存在至少一维,A的k元组该维大于B的k元组该维。(显然除了单方面碾压还有可能互有胜负)。
现在你可以安排比赛顺序以及钦定互有胜负的情况下的比赛结果。输了的立马淘汰,比赛不同时进行。对每个i求,加入第i个人后,哪些人有可能赢?
k ≤ 5 , n ≤ 1 0 5 k\le5,n\le10^5 k5,n105,k元组的每一维都是一个关于n的排列。
题解:
考虑一个朴素暴力。
若A单方面胜B则连单向边A->B,否则A和B互有胜负连双向边A–B。
那么判定一个人A是否有可能胜利,过程是:先让A能到的人都被A打败,然后被A打败的人去打败他们能打败但是A不能打败的人……依次类推,其实就是问能否从A出发遍历整张图。
显然如果A和B互有胜负那么二者没有本质区别。
并且有向边不可能构成环。
因此把互有胜负的边缩点之后,图是一张竞赛图,其拓扑序是个DAG。
考虑加进来一个人A。
如果A能够打败(不一定是单方面打败)某个人x,那么A就能打败缩点后x所在一坨点之后的一坨点。因此缩点后A能打败一个后缀的点。同理其会被一个前缀的点打败。二分这两个位置取交集,即存在和A互有胜负的缩点后的点的集合,这时把这些点合并即可。用一个Treap之类的维护一下。

#include<bits/stdc++.h>
#define gc getchar()
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define lint long long
#define mod 998244353
#define ull unsigned lint
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
namespace IO { const int S=(1<<20)+5;char buf[S],*H,*T;inline char getc() { if(H==T) T=(H=buf)+fread(buf,1,S,stdin);if(H==T) return -1;return *H++; }
    inline int inn() { int x=0,c=getc();while(!isdigit(c)) c=getc();while(isdigit(c)) x=(((x<<2)+x)<<1)+(c^'0'),c=getc();return x; } }using namespace IO;
char ss[20000000],tt[20];int ssl,ttl;
inline int show(int x) { if(!x) ss[++ssl]='0';for(ttl=0;x;x/=10) tt[++ttl]=char(x%10+'0');for(;ttl;ttl--) ss[++ssl]=tt[ttl];return ss[++ssl]='\n'; }
using namespace std;typedef pair<int,int> pii;typedef set<int>::iterator sit;const int N=100010;
struct node{
    static int k;int v[6];inline void input() { rep(i,1,k) v[i]=inn(); }
    inline int operator<(const node &n)const { int cnt=0;rep(i,1,k) if(v[i]<n.v[i]) cnt++;return cnt; }
    inline int operator>(const node &n)const { int cnt=0;rep(i,1,k) if(v[i]>n.v[i]) cnt++;return cnt; }
}p[N],MX,MN;int node::k=0;
struct node2{
    node mx,mn;int sz;node2() { init(); }inline int init() { return mx=MN,mn=MX,sz=0; }
    inline node2 operator+(const node2 &n)const
    {
        node2 m;
        rep(i,1,node::k) m.mx.v[i]=max(mx.v[i],n.mx.v[i]),m.mn.v[i]=min(mn.v[i],n.mn.v[i]);
        m.sz=sz+n.sz;return m;
    }
    inline node2 operator+=(const node2 &n) { return (*this)=(*this)+n; }
}tmp;int ch[N][2],rt,sz[N],fa[N],node_cnt;node2 v[N],val[N];ull key[N];
struct Rand{ int x,y;Rand(){x=1;}inline int operator()(){ return y=x,x+=x,(x>=mod?x-=mod:0),x+=y,(x>=mod?x-=mod:0),x; } }rnd;
inline int push_up(int x) { return val[x]=v[x]+val[ch[x][0]]+val[ch[x][1]],sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1; }
inline int new_node(const node2 &_v) { int x=++node_cnt;return ch[x][0]=ch[x][1]=fa[x]=0,key[x]=rnd(),v[x]=_v,push_up(x),x; }
inline int setc(int x,int y,int z) { if(!x) return fa[y]=0;ch[x][z]=y;if(y) fa[y]=x;return push_up(x); }
int _merge(int x,int y) { if(!x||!y) return x+y;if(key[x]<key[y]) return setc(x,_merge(ch[x][1],y),1),x;return setc(y,_merge(x,ch[y][0]),0),y; }
pii splitk(int x,int k) { if(!x||!k) return mp(0,x);pii t;if(k<=sz[ch[x][0]]) return t=splitk(ch[x][0],k),setc(x,t.sec,0),mp(t.fir,x);return t=splitk(ch[x][1],k-sz[ch[x][0]]-1),setc(x,t.fir,1),mp(x,t.sec); }
int queryL(int x,const node &p)
{
    if(!x||(val[x].mx<p)==node::k) return sz[x]+1;
    if(ch[x][0]&&val[ch[x][0]].mx>p) return queryL(ch[x][0],p);
    if(v[x].mx>p) return sz[ch[x][0]]+1;
    return sz[ch[x][0]]+1+queryL(ch[x][1],p);
}
int queryR(int x,const node &p)
{
    if(!x||(val[x].mn>p)==node::k) return 0;
    if(ch[x][1]&&val[ch[x][1]].mn<p) return sz[ch[x][0]]+1+queryR(ch[x][1],p);
    if(v[x].mn<p) return sz[ch[x][0]]+1;
    return queryR(ch[x][0],p);
}
int query(int s,int t) { pii a=splitk(rt,t),b=splitk(a.fir,s-1);return tmp+=val[b.sec],rt=_merge(b.fir,a.sec); }
int query(int x) { while(ch[x][1]) x=ch[x][1];return v[x].sz; }
inline int ins(int k,const node2 &_v) { pii a=splitk(rt,k);return rt=_merge(_merge(a.fir,new_node(_v)),a.sec); }
int main()
{
    int n=inn();node::k=inn();rep(i,1,n) p[i].input();
    rep(i,1,node::k) MX.v[i]=n+1,MN.v[i]=0;
    v[0].mx=MN,v[0].mn=MX,v[0].sz=0,val[0]=v[0];
    rep(i,1,n)
    {
        int s=queryL(rt,p[i]),t=queryR(rt,p[i]);
        tmp.mx=tmp.mn=p[i],tmp.sz=1;
        if(s<=t) query(s,t);ins(s-1,tmp),show(query(rt));
    }
    return fwrite(ss+1,sizeof(char),ssl,stdout),0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值