zoukankan      html  css  js  c++  java
  • [JZOJ4633] 【GDOI2017模拟7.15】萌萌哒

    题目

    描述

    在这里插入图片描述

    题目大意

    给你一个数列,接下来有许多个操作,使得区间[l1,r1][l_1,r_1][l2,r2][l_2,r_2]对应的位置染上同样的颜色(使得它们相同)。
    最后输出91019*10^{颜色数-1}


    思考历程

    首先看到这题就自然而然地往数据结构方面想(废话!)
    接着先想平衡树。既然要将这两个区间变成一样的,那就将它们各自放到子树中,然后对于两个子树的根打上标记。
    接下来问题就出现了,怎么维护?怎么下传?并且由于它可能多个标记在一起,这样的时间复杂度岂不是翻天了?
    然后我开始想,能不能将这些东西用同一棵子树来代替呢?
    想来想去都不能,因为在后面这棵子树总是会被拆开的。
    于是我又去想分块,想了一下后开始打,打了几句后突然发现——我没有办法保证这些块都是完全配对的!
    想不出来,最终颓废,拿了暴力3030分的好成绩。


    正解

    首先有WMY的强大分块做法,我大概听懂了,不过好复杂。
    我也没有打,也懒得打,何况他自己就被卡了常数。
    所以这里就先不介绍了。

    题解的做法是ST表。
    ST表是什么东西?就是打RMQRMQ时用的那个DP(数据结构?),也可以理解为倍增表。
    这题的ST表做法简单易懂,令人大开眼界。
    一个区间可以变成两个小区间(这两个小区间之间会有重合部分),两个小区间的并集就是这个大区间。
    对于要合并的两个区间,分别这样拆一下,然后对应的合并在一起。
    这和之前RMQ的道理是一样的。
    (当然,实际上也可以像倍增一样搞,这样的好处是没有重合部分,但对于这题并没有什么卵用)
    接下来就变成了合并对应的两个小区间。
    它们各自对应着一个ST表上的节点,所以就将这两个节点合并(当然用并查集)。
    接着将每个区间分别分成两份,递归下去合并。
    如果它们已经合并了,那就退出。(因为它们下面小区间的点已经被合并了。)

    听起来好像很暴力的样子。
    但实际上ST表上的节点只有O(nlgn)O(nlg n)个。
    由于合并过就退出,所以合并的次数顶多为O(nlgn)O(nlg n)
    那这样时间复杂度就得以保证了。
    总时间复杂度就是O(nlgn)O(nlg n)


    总结

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #define N 100010
    int n;
    struct Point{//这是为了方便操作而搞出来的指针式链表(真是造福人类)
    	Point *p;
    } st[N][17];
    Point *getfa(Point &x){
    	if (x.p==&x)
    		return &x;
    	return x.p=getfa(*x.p);
    }
    inline void merge(int x,int y,int k){
    	Point *xx=getfa(st[x][k]),*yy=getfa(st[y][k]);
    	if (xx==yy)
    		return;
    	xx->p=yy;
    	if (!k)
    		return;
    	merge(x,y,k-1),merge(x+(1<<k-1),y+(1<<k-1),k-1);
    }
    inline void connect(int x,int y,int len){
    	int k=log2(len);
    	merge(x,y,k);
    	merge(x+len-(1<<k),y+len-(1<<k),k);
    }
    int main(){
    	int T;
    	scanf("%d%d",&n,&T);
    	for (int i=1;i<=n;++i)
    		for (int j=0;j<17;++j)
    			st[i][j].p=&st[i][j];
    	while (T--){
    		int l1,r1,l2,r2;
    		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
    		connect(l1,l2,r1-l1+1);
    	}
    	int cnt=0;
    	for (int i=1;i<=n;++i)
    		if (getfa(st[i][0])==&st[i][0])
    			cnt++;
    	int ans=9;
    	for (int i=1;i<cnt;++i)
    		ans=ans*10ll%1000000007;
    	printf("%d
    ",ans);
    	return 0;
    }
    

    总结

    我真的想不到原来ST表还可以这样用……

  • 相关阅读:
    精读大道至简01
    mysql查询语句出现sending data耗时解决
    定位线上问题
    docker命令
    .net core2学习笔记
    centos下安装色彩scrapy
    mysql中id值被重置的情况
    MySql中的SHOW INDEX 查出的结果列代表的意义
    ELK安装
    使用PowerDesigner进行面向对象分析与UML建模(转)
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145219.html
Copyright © 2011-2022 走看看