\(\text{Knockout}\)

原题:\(\text{Boomerang Tournament}\)

题目描述

\(n\) 支队伍(\(n\)\(2^k\))进行淘汰赛,已知两两胜负关系。在每种对阵情况中,请计算出第 \(i\) 支队伍的最好名次和最劣名次。

\(1\le T,n\le 16\)\(G_{i,i}=0\),对于所有 \(i\neq j\),都有 \(G_{i,j}\neq G_{j,i}\)


需要注意名次的计算方式。将所有队伍按胜出场数从大到小排序,比如有两队并列第三,那么 下一种 场数的队伍就应该是第五名。

\(\rm sb\) 博主观察了老久的样例都没看懂…

解法

很玄学的是,我随机化了 \(10^5\) 次就拿到了 \(\rm 100pts\)代码戳这。但是我算了算,本质不同的排列是 \(\frac{16!}{2^8\cdot 2^4\cdot 2^2 \cdot 2}\) 的?就很离谱。

\(dp_{s,i}\) 是在 \(s\) 的集合中,\(i\) 是否能成为优胜者,其中 \(s\) 一定是 \(2\) 的幂。转移是比较简单的,我主要是分析一下复杂度。

考虑有四个 \(1\) 的集合共有 \(\binom{16}{4}=1820\) 个,有八个 \(1\) 的集合共有 \(\binom{16}{8}=12870\) 个,其余的都比较小了,不妨只分析 \(siz=16,8\) 的循环。

这样大概是 \(\mathcal O((1\cdot 12870\cdot 8^2/2+12870\cdot \binom{8}{4}\cdot 4^2/2)\cdot T)\),还是蛮稳的。

代码

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T> 
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9') 
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
} 

template <class T> 
inline void write(const T x) {
	if(x<0) {
		putchar('-');
		write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
}

#include <vector>
#include <cstring>
#include <iostream>
using namespace std;

const int maxn=20;

int n,g[maxn][maxn];
int dp[1<<16][17];
int mx[maxn],mn[maxn];
vector <int> mask[17];

void init() {
	for(int i=0;i<(1<<16);++i) {
		int t=__builtin_popcount(i);
		if(((t-1)&t)==0)
			mask[t].push_back(i);
	}
}

int lowbit(int x) {
	return x&-x;
}

int main() {
	freopen("knock.in","r",stdin);
	freopen("knock.out","w",stdout);
	int fk=0;
	init();
	for(int T=read(9);T;--T) {
		memset(dp,0,sizeof dp);
		n=read(9);
		for(int i=0;i<n;++i) {
			mx[i]=0,mn[i]=n;
			dp[1<<i][i]=1;
			for(int j=0;j<n;++j)
				g[i][j]=read(9);
		}
		printf("Case #%d:\n",++fk);
		int dep=1;
		for(int siz=2;siz<=n;siz<<=1,++dep)
			for(int uu=0;uu<mask[siz].size();++uu) {
				int U=mask[siz][uu];
				if(U>=(1<<n)) break;
				for(int ss=0;ss<mask[siz>>1].size();++ss) {
					int S=mask[siz>>1][ss];
					if((S&U)!=S) continue;
					int T=U^S;
					if(S>T) continue;
					int s=S;
					while(s) {
						int i=__builtin_ctz(s);
						s-=lowbit(s);
						if(!dp[S][i]) continue;
						int t=T;
						while(t) {
							int j=__builtin_ctz(t);
							t-=lowbit(t);
							if(!dp[T][j]) continue;
							if(g[i][j]) {
								dp[U][i]=1;
								mx[i]=max(mx[i],dep);
								mn[j]=min(mn[j],dep-1);
							}
							else {
								dp[U][j]=1;
								mx[j]=max(mx[j],dep);
								mn[i]=min(mn[i],dep-1);
							}
						}
					}
				}
			}
		--dep;
		for(int i=0;i<n;++i) {
			int a=(mx[i]==dep)?0:(1<<(dep-mx[i]-1));
			int b=(mn[i]>=dep)?0:(1<<(dep-mn[i]-1));
			printf("%d %d\n",a+1,b+1);
		}
	}
	return 0;
}

\(\text{[ZJOI 2012] }\)小蓝的好友

解法

首先转化成一条鱼都没有的矩形有多少个。

考虑添加一条与 \(x\) 轴平行的扫描线,从上往下移,那么扫描线以上的部分可以形成一个柱状图(遇见鱼 \((x,y)\) 后将经过 \(x\) 的柱高度置为 \(0\))。

对于固定的柱状图,我们可以以 \(x\)\(\rm key\),高度为 \(\rm val\) 建立一棵笛卡尔树。对于每个宽为 \(w\),长为 \(h\) 的完整矩形,内部矩形计算公式是 \(\frac{w(w+1)}{2}\cdot h\)(相当于最左边的 \(x\)\(x,x+1,...,x+w-1\) 配对,依此类推)。单次计算是 \(\mathcal O(n)\) 的。

如何优化?考虑每次往下移扫描线,所有柱高度加一,遇到鱼时柱高度变为 \(0\),而且还要维护笛卡尔树。用 \(\rm treap\) 啊!

当某个柱高度变为 \(0\) 时,\(\rm val\) 改变导致堆的条件不满足,所以需要 split() 一下。因为鱼是随机的,所以是 \(\mathcal O(n\log n)\) 的。

另外这题也可以用 \(\rm splay\) 做,但在修改时不能使用 splay() 函数:因为要维护小根堆的性质,双旋可能破坏这一性质,所以只能 rotate() 到根(不过复杂度就很奇怪了。

所以还是得会 \(\text{fhq_treap}\).

代码

\(\text{fhq_treap}\)

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T> 
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9') 
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
} 

template <class T> 
inline void write(const T x) {
	if(x<0) {
		putchar('-');
		write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
}

#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn=100005;

ll S(int n) {
	return 1ll*n*(n+1)/2;
}

int r,c,n;
struct fish {
	int x,y;
	
	bool operator < (const fish &t) const {
		return y<t.y;
	}
} s[maxn];
struct fhq_treap {
	int idx,rt;
	struct node {
		int h,ls,rs,siz,la;
		ll ans;
	} t[maxn];
	
	int NewNode(int H) {
		t[++idx].siz=1;
		t[idx].h=H;
		return idx;
	}
	
	void add(int o,int k) {
		if(!o) return;
		t[o].la+=k;
		t[o].h+=k;
	}
	
	void pushDown(int o) {
		if(!o or !t[o].la)
			return;
		add(t[o].ls,t[o].la);
		add(t[o].rs,t[o].la);
		t[o].la=0;
	}
	
	void pushUp(int o) {
		if(!o) return;
		t[o].ans=0;
		t[o].siz=t[t[o].ls].siz+t[t[o].rs].siz+1;
		if(t[o].ls)
			t[o].ans+=t[t[o].ls].ans+S(t[t[o].ls].siz)*(t[t[o].ls].h-t[o].h);
		if(t[o].rs)
			t[o].ans+=t[t[o].rs].ans+S(t[t[o].rs].siz)*(t[t[o].rs].h-t[o].h);
	}
	
	void split(int o,int k,int &x,int &y) {
		if(!o) x=y=0;
		else {
			pushDown(o);
			if(t[t[o].ls].siz+1<=k) {
				x=o;
				split(t[o].rs,k-t[t[o].ls].siz-1,t[o].rs,y);
			}
			else {
				y=o;
				split(t[o].ls,k,x,t[o].ls);
			}
			pushUp(o);
		}
	}
	
	int merge(int x,int y) {
		if(!x or !y)
			return x|y;
		if(t[x].h<t[y].h) {
			pushDown(x);
			t[x].rs=merge(t[x].rs,y);
			pushUp(x);
			return x;
		}
		else {
			pushDown(y);
			t[y].ls=merge(x,t[y].ls);
			pushUp(y);
			return y;
		}
	}
	
	void build(int n) {
		for(int i=1;i<=n;++i)
			rt=merge(rt,NewNode(0));
	}
} T;

int main() {
	r=read(9),c=read(9),n=read(9);
	for(int i=1;i<=n;++i)
		s[i]=(fish){read(9),read(9)};
	T.build(r);
	sort(s+1,s+n+1);
	ll ans=S(r)*S(c);
	int pos=1;
	for(int i=1;i<=c;++i) {
		T.add(T.rt,1);
		while(pos<=n and s[pos].y==i) {
			int cut=s[pos].x,a,b,c;
			T.split(T.rt,cut-1,a,b);
			T.split(b,1,b,c);
			T.t[b].h=0;
			T.rt=T.merge(T.merge(a,b),c);
			++pos;
		}
		ans-=T.t[T.rt].ans+S(T.t[T.rt].siz)*T.t[T.rt].h;
		// 最后还要计算最下面完整的一块
	}
	print(ans,'\n');
	return 0;
}

\(\rm splay\)

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
    T x=0; char s; bool f=0;
    while((s=getchar())>'9' || s<'0')
        f |= (s=='-');
    while(s>='0' && s<='9')
        x = (x<<1)+(x<<3)+(s^48),
        s = getchar();
    return f?-x:x;
}

template <class T>
inline void write(T x) {
    static int writ[50],w_tp=0;
    if(x<0) putchar('-'),x=-x;
    do writ[++w_tp]=x-x/10*10,x/=10; while(x);
    while(putchar(writ[w_tp--]^48),w_tp);
}

#include <algorithm>
using namespace std;
typedef long long ll;

inline ll S(int x) { return (1ll*x*(x+1))>>1; }

const int maxn = 1e5+5;

int r,c,n,rt;
struct fish { 
    int x,y; 
    bool operator < (const fish& t) const {
        return y<t.y;
    }
} f[maxn];
struct node {
    int son[2],fa,h,s,la; ll ans;
} t[maxn];

bool dir(int o) { return t[t[o].fa].son[1]==o; }
void add(int o,int f,bool d) {
    if(o) t[o].fa=f;
    if(f) t[f].son[d]=o;
}
void inc(int o,int k) {
    if(!o) return;
    t[o].la+=k, t[o].h+=k;
}
void pushDown(int o) {
    if(!o || !t[o].la) return;
    inc(t[o].son[0],t[o].la);
    inc(t[o].son[1],t[o].la);
    t[o].la=0;
}
void pushUp(int o) {
    if(!o) return;
    int lc=t[o].son[0], rc=t[o].son[1];
    t[o].s = t[lc].s+t[rc].s+1; t[o].ans=0;
    if(lc) t[o].ans = t[lc].ans+S(t[lc].s)*(t[lc].h-t[o].h);
    if(rc) t[o].ans += t[rc].ans+S(t[rc].s)*(t[rc].h-t[o].h);
}
void rotate(int o) {
    int f=t[o].fa, ff=t[f].fa;
    bool d=dir(o), fd=dir(f);
    add(t[o].son[d^1],f,d);
    add(f,o,d^1), add(o,ff,fd);
    pushUp(f), pushUp(o);
}
void pushAll(int o) {
    static int stk[maxn],tp;
    stk[tp=1] = o;
    while(t[o].fa) stk[++tp] = (o=t[o].fa);
    while(tp) pushDown(stk[tp--]);
}
void build(int &o,int l,int r) {
    if(l>r) return;
    o = l+r>>1;
    build(t[o].son[0],l,o-1);
    build(t[o].son[1],o+1,r);
    if(t[o].son[0]) t[t[o].son[0]].fa=o;
    if(t[o].son[1]) t[t[o].son[1]].fa=o;
    pushUp(o);
}
void printTree(int o) {
    if(!o) return;
    printf("ID(%d) son(%d %d) %lld %d %d\n",o,t[o].son[0],t[o].son[1],t[o].ans,t[o].s,t[o].h);
    pushDown(o);
    printTree(t[o].son[0]);
    printTree(t[o].son[1]);
}

int main() {
    r=read(9), c=read(9), n=read(9);
    for(int i=1;i<=n;++i)
        f[i].x=read(9), f[i].y=read(9);
    sort(f+1,f+n+1); ll ans = S(r)*S(c); 
    build(rt,1,r); int j=1;
    for(int i=1;i<=c;++i) {
        inc(rt,1);
        while(j<=n && f[j].y==i) {
            int o=f[j].x; pushAll(o);
            while(t[o].fa) rotate(o);
            t[o].h=0; rt=o;
            pushUp(o); ++ j;
        }
        ans -= t[rt].ans+S(t[rt].s)*t[rt].h; 
    }
    print(ans,'\n');
    return 0;
}
posted on 2021-09-03 08:14  Oxide  阅读(35)  评论(0编辑  收藏  举报