zoukankan      html  css  js  c++  java
  • Codeforces 1375H Set Merging (分块)

    题目链接

    https://codeforces.com/contest/1375/problem/H

    题解

    首先注意到 (2.2 imes 10^6approx 2nsqrt q),因此想到分块。
    考虑对值域进行分块,每块内值域连续,位置保持相对不变,大小为 (B),分成 (frac{n}{B}) 块。
    那么我们可以对每个块内的 (frac{B(B+1)}{2}) 个区间中的每个合并出一个集合,然后对于每组询问在每个块内求出对应的区间,并将每个块内的集合合并到一起。
    后一部分显然需要 (frac{nq}{B}) 次操作,考虑前一部分怎么做。

    考虑对值域进行分治,每次处理一个值域区间对应的位置,那么对于当前值域区间的一个位置区间,可以直接由两个子值域区间的对应位置区间合并得到。
    总合并次数的递归式为 (T(n)=2T(frac{n}{2})+frac{n^2}{2}) (省略了渐进意义上较小的部分),得 (T(n)approx n^2). 即每个块内的预处理需要 (B^2) 次操作,总共就是 (nB) 次。

    综合两部分,总共操作次数是 (nB+frac{nq}{B}),取 (B=sqrt q) 则可以做到总次数为 (2nsqrt q).

    代码

    #include<bits/stdc++.h>
    #define llong long long
    #define mkpr make_pair
    #define x first
    #define y second
    #define iter iterator
    #define riter reversed_iterator
    #define y1 Lorem_ipsum_
    #define tm dolor_sit_amet_
    #define pii pair<int,int>
    using namespace std;
    
    inline int read()
    {
    	int x = 0,f = 1; char ch = getchar();
    	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
    	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
    	return x*f;
    }
    
    const int mxN = 1<<12;
    const int mxM = 1<<16;
    const int mxSIZ = 2.2e6;
    const int mxB = 1<<8;
    int a[mxN+3],ai[mxN+3];
    int n,m,siz,B;
    int ans[mxM+3];
    vector<pii> way;
    
    int merge(int u,int v) {if(!u||!v) {return u+v;} way.push_back(mkpr(u,v)); return ++siz;}
    
    int f[mxB*2+3][mxB+3][mxB+3];
    struct Block
    {
    	int len,al,ar; int p[mxN+3];
    	void solve(int u,int l,int r,vector<int> vec)
    	{
    		if(l==r) {f[u][1][1] = ai[vec[1]]; return;}
    		int mid = (l+r)>>1; vector<int> vecl(1),vecr(1);
    		for(int i=1; i<vec.size(); i++) {if(vec[i]<=mid) {vecl.push_back(vec[i]);} else {vecr.push_back(vec[i]);}}
    		solve(u<<1,l,mid,vecl); solve(u<<1|1,mid+1,r,vecr);
    		for(int i=1,xl=1,xr=1; i<vec.size(); xl+=(vec[i]<=mid),xr+=(vec[i]>mid),i++)
    		{
    			for(int j=i-1,yl=xl-1,yr=xr-1; j<vec.size(); j++,yl+=(vec[j]<=mid),yr+=(vec[j]>mid))
    			{
    				if(j==i-1) continue;
    				f[u][i][j] = merge(f[u<<1][xl][yl],f[u<<1|1][xr][yr]);
    			}
    		}
    	}
    	int id[mxN+3],qwq[mxB+3][mxB+3];
    	void build()
    	{
    		vector<int> vec(1);
    		for(int i=1; i<=n; i++) {if(a[i]>=al&&a[i]<=ar) {vec.push_back(a[i]); id[i] = id[i-1]+1;} else {id[i] = id[i-1];}}
    		solve(1,al,ar,vec);
    		for(int i=1; i<vec.size(); i++) for(int j=1; j<vec.size(); j++) qwq[i][j] = f[1][i][j];
    	}
    	int query(int l,int r) {return qwq[id[l-1]+1][id[r]];}
    } blo[18];
    
    int main()
    {
    	n = read(),m = read(),siz = n,B = min(n,mxB);
    	for(int i=1; i<=n; i++) a[i] = read(),ai[a[i]] = i; siz = n;
    	for(int i=1; (i-1)*B+1<=n; i++)
    	{
    		blo[i].al = (i-1)*B+1,blo[i].ar = i*B;
    		blo[i].build();
    	}
    	for(int q=1; q<=m; q++)
    	{
    		int l = read(),r = read(); ans[q] = 0;
    		for(int i=1; i<=(n-1)/B+1; i++)
    		{
    			ans[q] = merge(ans[q],blo[i].query(l,r));
    		}
    	}
    	printf("%d
    ",siz);
    	for(int i=0; i<way.size(); i++) printf("%d %d
    ",way[i].x,way[i].y);
    	for(int i=1; i<=m; i++) printf("%d ",ans[i]); puts("");
    	return 0;
    }
    
  • 相关阅读:
    学习进度 -- 2019.6.20
    剑指Offer的学习笔记(C#篇)-- 平衡二叉树(二叉树后序遍历递归详解版)
    剑指Offer的学习笔记(C#篇)-- 二叉树的深度(详讲递归)
    剑指Offer的学习笔记(C#篇)-- 数字在排序数组中出现的次数
    if-else判断语句中经常犯的一个错误
    剑指Offer的学习笔记(C#篇)-- 旋转数组的最小数字
    二叉树遍历基础 -- 递归与非递归的实现方法
    剑指Offer的学习笔记(C#篇)-- 序列化二叉树
    剑指Offer的学习笔记(C#篇)-- 对称的二叉树
    剑指Offer的学习笔记(C#篇)-- 二叉树的下一个节点(好理解版本)
  • 原文地址:https://www.cnblogs.com/suncongbo/p/13275401.html
Copyright © 2011-2022 走看看