• 【loj



    description

    小 R 与小 W 在玩游戏。

    他们有一个边数为 (n) 的凸多边形,其顶点沿逆时针方向标号依次为 (1,2,3,dots,n)。最开始凸多边形中有 (n) 条线段,即多边形的 (n) 条边。这里我们用一个有序数对 ((a,b))(其中 (a<b))来表示一条端点分别为顶点 (a,b) 的线段。

    在游戏开始之前,小 W 会进行一些操作。每次操作时,他会选中多边形的两个互异顶点,给它们之间连一条线段,并且所连的线段不会与已存的线段重合、相交(只拥有一个公共端点不算作相交)。他会不断重复这个过程,直到无法继续连线,这样得到了状态 (S_0)(S_0) 包含的线段为凸多边形的边与小 W 连上的线段,容易发现这些线段将多边形划分为一个个三角形区域。对于其中任意一个三角形,其三个顶点为 (i,j,k(i<j<k)),我们可以给这个三角形一个标号 (j),这样一来每个三角形都被标上了 (2,3,dots,n-1) 中的一个,且没有标号相同的两个三角形。

    小 W 定义了一种「旋转」操作:对于当前状态,选定 (4) 个顶点 (a,b,c,d),使其满足 (1leq a<b<c<dleq n) 且它们两两之间共有 (5) 条线段—— ((a,b),(b,c),(c,d),(a,d),(a,c)),然后删去线段 ((a, c)),并连上线段 ((b,d))。那么用有序数对 ((a,c)) 即可唯一表示该次「旋转」。我们称这次旋转为 ((a,c)) 「旋转」。显然每次进行完“旋转”操作后多边形中依然不存在相交的线段。

    当小 W 将一个状态作为游戏初始状态展示给小 R 后,游戏开始。游戏过程中,小 R 每次可以对当前的状态进行「旋转」。在进行有限次「旋转」之后,小 R 一定会得到一个状态,此时无法继续进行「旋转」操作,游戏结束。那么将每一次「旋转」所对应的有序数对操作顺序写下,得到的序列即为该轮游戏的操作方案

    为了加大难度,小 W 以 (S_0) 为基础,产生了 (m) 个新状态。其中第 (i) 个状态 (S_i) 为对 (S_0) 进行一次「旋转」操作后得到的状态。你需要帮助小 R 求出分别以 (S_0,S_1,dots,S_m) 作为游戏初始状态时,小 R 完成游戏所用的最少「旋转」次数,并根据小 W 的心情,有时还需求出旋转次数最少不同操作方案数。由于方案数可能很大,输出时请对 (10^9+7) 取模。

    原题传送门。

    solution

    把三角剖分每条边 ((a, b)) 看成区间 ((a, b))(不妨假设 (a < b))。
    则根据三角剖分的性质,任意两区间要么相离,要么包含,且存在区间 ((1,2),(2,3)dots,(n-1,n))((1,n))

    考虑建树:区间 ((l, r)) 的儿子为它所包含的极大区间 ((p, q))(不存在区间介于 ((p, q))((l, r)) 之间)。
    那么该树叶子为 ((1,2),(2,3)dots,(n-1,n)),根为 ((1,n))

    考虑这棵树的特殊性质:不存在儿子个数恰好为 (1) 的非叶结点,非叶结点数量为 (n - 2)(算上根结点),叶子数量为 (n - 1)
    那么显然这棵树是二叉树,且非叶结点的儿子个数恰为 2。

    考虑旋转操作对应到树上的含义:右旋非叶结点(真就“旋转”操作啊)
    考虑终止状态的含义:非叶结点形成一条右链。也就是说不存在非叶结点 x 在 y 的左子树内。
    我们可以先删掉叶结点。

    每次旋转最多会使一个点不在某点左子树中,且总可以旋转最右链上的点使得左子树点减少。
    那么最小次数即最右链上的点的左子树大小总和 cnt。

    考虑方案数,每次可以任选最右链上某一点进行旋转。通过简单的组合推导可得方案数为:

    [frac{cnt!}{prod_{x是左子树点}siz_x} ]

    一次右旋只会影响 (O(1)) 个点,简单分类回答询问即可。

    accepted code

    #include <map>
    #include <cstdio>
    #include <vector>
    #include <cassert>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    typedef pair<int, int> pii;
    #define pr make_pair
    #define last(v) v[v.size() - 1]
    #define all(v) v.begin(), v.end()
    #define pb(x) push_back(x)
    
    const int MOD = int(1E9) + 7;
    const int MAXN = 100000;
    
    inline int add(int x, int y) {x += y; return x >= MOD ? x - MOD : x;}
    inline int sub(int x, int y) {x -= y; return x < 0 ? x + MOD : x;}
    inline int mul(int x, int y) {return (int)(1LL * x * y % MOD);}
    
    int fct[MAXN + 5], inv[MAXN + 5];
    void init() {
    	fct[0] = 1; for(int i=1;i<=MAXN;i++) fct[i] = mul(fct[i - 1], i);
    	inv[1] = 1; for(int i=2;i<=MAXN;i++) inv[i] = sub(0, mul(inv[MOD % i], MOD / i));
    }
    
    map<pii, int>mp; int ncnt;
    int id(int l, int r) {
    	if( mp.count(pr(l, r)) ) return mp[pr(l, r)];
    	else return mp[pr(l, r)] = (++ncnt);
    }
    vector<int>vl[MAXN + 5], vr[MAXN + 5];
    
    bool rgt[MAXN + 5]; int siz[MAXN + 5], ch[2][MAXN + 5], fa[MAXN + 5];
    int build(int l, int r, bool f) {
    	if( l + 1 == r ) return 0;
    	int x = id(l, r), m = last(vr[l]);
    	assert(last(vl[r]) == m);
    	vr[l].pop_back(), vl[m].pop_back(), fa[ch[0][x] = build(l, m, false)] = x;
    	vr[m].pop_back(), vl[r].pop_back(), fa[ch[1][x] = build(m, r, f)] = x;
    	siz[x] = siz[ch[0][x]] + siz[ch[1][x]] + 1, rgt[x] = f;
    	return x;
    }
    
    bool cmp(const int &x, const int &y) {return x > y;}
    int main() {
    	init(); int W, n, m; scanf("%d%d", &W, &n);
    	for(int i=1;i<n;i++) vr[i].pb(i + 1);
    	for(int i=n;i>1;i--) vl[i].pb(i - 1);
    	for(int i=1;i<=n-3;i++) {
    		int x, y; scanf("%d%d", &x, &y);
    		vl[x].pb(y), vr[x].pb(y), vl[y].pb(x), vr[y].pb(x);
    	}
    	for(int i=1;i<=n;i++)
    		sort(all(vr[i])), sort(all(vl[i]), cmp);
    	build(1, n, true);
    	
    	int ans = 1, cnt = 0;
    	for(int i=1;i<=ncnt;i++)
    		if( !rgt[i] ) cnt++, ans = mul(ans, inv[siz[i]]);
    	if( W == 1 ) printf("%d %d
    ", cnt, mul(ans, fct[cnt]));
    	else printf("%d
    ", cnt);
    	
    	scanf("%d", &m);
    	for(int i=1;i<=m;i++) {
    		int l, r; scanf("%d%d", &l, &r);
    		int x = id(l, r), cnt1, ans1;
    		if( rgt[fa[x]] )
    			cnt1 = cnt - 1, ans1 = mul(mul(ans, fct[cnt - 1]), siz[x]);
    		else {
    			int p = ch[1][fa[x]], q = ch[1][x];
    			cnt1 = cnt, ans1 = mul(mul(ans, fct[cnt]), mul(siz[x], inv[siz[p] + siz[q] + 1]));
    		}
    		if( W == 1 ) printf("%d %d
    ", cnt1, ans1);
    		else printf("%d
    ", cnt1);
    	}
    }
    

    details

    当然也可以不用像这样显性地建树。
    事实上这道题转化问题的方法比较多,这里只是采用了我使用的一种。

    最后代码实现比较简单,对问题一环套一环的分析更有趣些。

  • 相关阅读:
    [重回VB6]简单的QQWeb网游辅助工具开发之旅1、序言,另类的QQ登陆方法
    QQ和360大战我的观点
    不用IE Test ,快速对IE兼容性进行测试
    第八届软件设计大赛完全作品赛前评析与剧透
    屌丝如何分发大文件(大于1G)
    NetDog 酷炫版 0.1测试版发布
    Jquery制作的页码插件
    使用Html5+CSS3摆脱JS做带提示文字的输入框
    在nhibernate中,Load相同ID的实体对象的时候出错的问题!
    fieldset,legend
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/13092941.html
Copyright © 2020-2023  润新知