• Luogu P3295 【[SCOI2016]萌萌哒】


    Description

    传送门


    Solution

    首先先考虑如果限制不是区间,而是告诉你两个位置的字符相等的做法。

    这样的话,我们可以把相等的位置加进一些集合里,最后答案就是(9 imes 10 ^ {num - 1}),其中(num)代表不同的集合个数,这个可以简单地用并查集维护连通性。

    那么如果是两个区间的话,考虑最暴力的做法,直接把区间里对应的点加进一个集合然后按单点跑,这样肯定是过不了的,考虑优化。

    首先发现区间相等这个性质满足结合律,即

    [Merge(l1, r1, l2, r2) = Merge(Merge(l3, r3, l4, r4), Merge(l5, r5, l6, r6)) ]

    其中满足

    [[l3, r3] cup [l4, r4] = [l1, r1] ]

    [[l5, r5] cup [l6, r6] = [l2, r2] ]

    这样的话我们就不用一个一个枚举区间里的位置加进并查集,而是可以像(st)表一样将每两个区间拆成四个(2)的幂次的形式的区间的并,将这四个区间分别加入两个并查集。

    所有的区间都拆完之后我们发现现在维护连通性的都是(2)的幂长度的区间,而我们需要的是长度为(1)的区间的连通信息。这样我们可以考虑将区间下放,将每个区间拆成两个等长的区间,发现对于一个并查集里的任意一个区间,只把它和这个集合的代表元素拆开就行。

    所以每一层最多下放(n)个区间,一共有(log_n)层,那么复杂度就是(O(nlog_n))的。当然这里没有考虑并查集的复杂度。


    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int N = 100000;
    const int MOD = 1000000007;
    
    int n, m, fa[N + 50][25], lg[N + 50], ans;
    
    int Ksm(int a, int b)
    {
    	int tmp = 1;
    	b = b % (MOD - 1);
    	while (b)
    	{
    		if (b & 1) tmp = 1LL * tmp * a % MOD;
    		a = 1LL * a * a % MOD;
    		b >>= 1;
    	}
    	return tmp;
    }
    
    int Find(int x, int k)
    {
    	return fa[x][k] == x ? x : fa[x][k] = Find(fa[x][k], k);
    }
    
    void Merge(int l1, int l2, int k)
    {
    //	cout << l1 << " " << l2 << " " << k << endl;
    	int fu = Find(l1, k), fv = Find(l2, k);
    	if (fu == fv) return;
    	fa[fu][k] = fv;
    	return;
    }
    
    void Pushdown()
    {
    	for (int j = lg[n]; j >= 1; j--)
    		for (int i = 1; i + (1 << j) <= n; i++)
    		{
    			int x = Find(i, j);
    			if (x == i) continue;
    	//		cout << i << " " << x << endl;
    			Merge(i, x, j - 1);
    			Merge(i + (1 << j - 1), x + (1 << j - 1), j - 1);
    		}
    	return;
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	lg[0] = -1;
    	for (int i = 1; i <= n; i++) lg[i] = lg[i >> 1] + 1;
    	for (int i = 1; i <= n; i++)
    		for (int j = 0; j <= lg[n]; j++)
    			fa[i][j] = i;
    	for (int i = 1, l1, l2, r1, r2; i <= m; i++)
    	{
    		scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
    		int length = r1 - l1 + 1;
    		Merge(l1, l2, lg[length]);
    		Merge(r1 - (1 << lg[length]) + 1, r2 - (1 << lg[length]) + 1, lg[length]); 
    	}
    	Pushdown();
    	for (int i = 1; i <= n; i++) if (fa[i][0] == i) ans++;
    	printf("%d", 9LL * Ksm(10, ans - 1) % MOD);
    	return 0;
    } 
    
  • 相关阅读:
    FP-growth算法思想和其python实现
    HDInsight Command
    OpsMgr Connector 20070
    Exchange: How to get Mailbox size in Exchange Shell?
    Get members for ‘Dynamic Distribution Group’
    Hadoop 学习之路-开篇
    Office365 配置完成ADFS之后修改密码之后需要删除登陆信息
    添加只读访问权限到邮箱里的所有文件夹
    使用 Windows PowerShell 连接到 Lync Online
    windows 7无法打补丁
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13097718.html
Copyright © 2020-2023  润新知