zoukankan      html  css  js  c++  java
  • [SCOI2016]萌萌哒

    嘟嘟嘟


    这题我连(O(n ^ 2))的都没想出来……
    刚开始看到字符串就想SAM,然后发现连具体的串都没有,觉得再用字符串算法的话就有点扯了。


    首先应该发现一点,如果然两个区间相等,实际上就是两个区间对应位置的数相等。所以我们把区间拆成一个个单独的位置,然后用并查集维护相等的位置集合即可。
    那么答案就是9 * (联通块数量 - 1),因为第一位不能为0.


    修改复杂度(O(n)),查询复杂度也是(O(n)),只能得30分。
    因为这题只有一次查询,所以想办法把修改改为(O(logn)),查询改为(O(nlogn))
    然后有人就想到了倍增。
    (p[j][i])表示区间([i, i + (1 << j) - 1])所在的集合。这样修改的时候就跟倍增lca很像了,往后跳合并即可。
    查询的时候就比较妙,就是把大区间的合并信息下传给小区间,就像下图一样:

    即两个大区间的左儿子和右儿子分别合并。(刚开始我理解成左右儿子都合并到大区间上……)
    写的时候可以记录每一个区间的左右儿子是谁,就像这位的代码一样:学姐的代码
    然后我从题解里发现了一个更简单的写法,详情参见下方代码。


    实现的时候就是把并查集的所有操作都加一位。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<stack>
    #include<queue>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 1e5 + 5;
    const int N = 17;
    const ll mod = 1e9 + 7;
    inline ll read()
    {
      ll ans = 0;
      char ch = getchar(), last = ' ';
      while(!isdigit(ch)) last = ch, ch = getchar();
      while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
      if(last == '-') ans = -ans;
      return ans;
    }
    inline void write(ll x)
    {
      if(x < 0) x = -x, putchar('-');
      if(x >= 10) write(x / 10);
      putchar(x % 10 + '0');
    }
    
    int n, m;
    
    int p[N + 2][maxn];
    In void init()
    {
      for(int i = 1; i <= n; ++i)
        for(int j = 0; j <= N; ++j) p[j][i] = i;
    }
    In int Find(int x, int k)
    {
      return x == p[k][x] ? x : p[k][x] = Find(p[k][x], k);
    }
    In void merge(int x, int y, int k)
    {
      int px = Find(x, k), py = Find(y, k);
      if(px ^ py) p[k][px] = py;
    }
    
    int main()
    {
      freopen("2.in", "r", stdin);
      //freopen("ha.out", "w", stdout);
      n = read(), m = read();
      init();
      for(int i = 1; i <= m; ++i)
        {
          int l1 = read(), r1 = read(), l2 = read(), r2 = read();
          for(int j = N; j >= 0 ; --j)
    	if((1 << j) <= r1 - l1 + 1)
    	  {
    	    merge(l1, l2, j);
    	    l1 += (1 << j), l2 += (1 << j);
    	  }
        }
      for(int j = N; j; --j)
        for(int i = 1; i + (1 << j) - 1 <= n; ++i)
          {
    	merge(i, Find(i, j), j - 1);
    	merge(i + (1 << (j - 1)), Find(i, j) + (1 << (j - 1)), j - 1);
          }
      int tot = 0; ll ans = 9;
      for(int i = 1; i <= n; ++i) if(Find(i, 0) == i) ++tot;
      for(int i = 1; i < tot; ++i) ans = ans * 10 % mod;
      write(ans), enter;
      return 0;
    }
    
  • 相关阅读:
    Ubuntu 16 安装redis客户端
    crontab 参数详解
    PHP模拟登录发送闪存
    Nginx配置端口访问的网站
    Linux 增加对外开放的端口
    Linux 实用指令之查看端口开启情况
    无敌的极路由
    不同的域名可以指向同一个项目
    MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error
    Redis 创建多个端口
  • 原文地址:https://www.cnblogs.com/mrclr/p/10594429.html
Copyright © 2011-2022 走看看