• [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;
    }
    
  • 相关阅读:
    单链表反转
    C++面试题
    堆排序
    1链表:回文链表(leetcode 234)
    深信服社招linux岗面试记录
    腾讯后台开发社招记录(电话面试)
    小米社招ATE岗位记录
    诺基亚社招C++面试记录
    腾讯后台开发社招面试记录
    makefile笔记
  • 原文地址:https://www.cnblogs.com/mrclr/p/10594429.html
Copyright © 2020-2023  润新知