• P4168 [Violet]蒲公英


    P4168 [Violet]蒲公英

    题目

    传送门

    思路

    经典的在线求众数问题

    预处理

    首先,离散化时绝对跑不掉的,设a为离散化后的序列,c为原序列,b为离散化辅助数组

    struct node {
    	int dat , id;
    }b[nn];
    bool cmp(node a , node b){return a.dat < b.dat;}
    
    	//离散化 
    	for(int i = 1 ; i <= n ; i++)
    		b[i].dat = a[i] , b[i].id = i;
    	sort(b + 1 , b + n + 1 , cmp);
    	int cnt_ = 0;
    	for(int i = 1 ; i <= n ; i++) {
    		if(b[i].dat != b[i - 1].dat) cnt_++;
    		a[b[i].id] = cnt_;
    	}
    

    把序列a分成t块(提前说明t约为3次根号下n,具体原因在时间复杂度中讲),每段长度为len=n/t,然后是分块的常规操作:设L,R表示每一块的左右端点,pos表示每个点所属分块

    	t = 0;
    	while(t * t * t < n)++t;
    	len = n / t;
    	
    	L[1] = 1 , R[1] = len;
    	for(int i = 2 ; i <= t ; i++)
    		L[i] = R[i - 1] + 1,
    		R[i] = len * i;
    	if(R[t] < n)
    		++t , L[t] = R[t - 1] + 1 , R[t] = n;
    	
    	for(int i = 1 ; i <= t ; i++)
    		for(int j = L[i] ; j <= R[i] ; j++)
    			pos[j] = i;
    

    此外,我们设cnt[i][j][k]表示数字k在第i个块到第j个块出现的次数,zs[i][j]表示第i个块到到j个块的众数的下标(原谅我不知道众数的英文就直接上拼音了)

    	//预处理cnt 和 众数 
    	for(int i = 1 ; i <= t ; i++)
    		for(int j = i ; j <= t ; j++)
    			for(int k = L[i] ; k <= R[j] ; k++) {
    				++cnt[i][j][a[k]];
    				if(cnt[i][j][a[k]] > cnt[i][j][a[zs[i][j]]] || (cnt[i][j][a[k]] == cnt[i][j][a[zs[i][j]]] && a[k] < a[zs[i][j]]))
    					zs[i][j] = k;
    			}
    

    对于每一个询问

    若l,r属于同一块,则直接暴力

    		for(int i = l ; i <= r ; i++) {
    			++tmp_cnt[a[i]];
    			if(tmp_cnt[a[i]] > tmp_cnt[a[ans]] || (tmp_cnt[a[i]] == tmp_cnt[a[ans]] && a[i] < a[ans]))
    				ans = i;
    		}
    		for(int i = l ; i <= r ; i++)
    			--tmp_cnt[a[i]];//这里直接减应该比memset快(我没试过),memset是针对整个数组(就是O(m*n)了),而此时r-l不超过len,是根号级别
    		return c[ans];
    

    对于其他情况:

    p=pos[l],q=pos[r]

    和分块模板一样,我们把[l,r]分为:开头:[l,R[p]),中间:[L[p+1],R[q-1]],结尾:(L[q],r](这里注意下括号的意义,有的是下标,有的是区间)

    显然,最终众数出为块p+1~q-1的众数,或开头,结尾两段的数之中

    因此,我们令ans=zs[p+1][q-1],然后在cnt[p+1][q-1][]的基础上加上开头,结尾两段的数,直接统计答案即可

    	++p , --q;//方便起见
    	ans = zs[p][q];
    	for(int i = l ; i <= R[p - 1] ; i++) {
    		++cnt[p][q][a[i]];
    		if(cnt[p][q][a[i]] > cnt[p][q][a[ans]] || (cnt[p][q][a[i]] == cnt[p][q][a[ans]] && a[i] < a[ans]))
    			ans = i;
    	}
    	for(int i = L[q + 1] ; i <= r ; i++) {
    		++cnt[p][q][a[i]];
    		if(cnt[p][q][a[i]] > cnt[p][q][a[ans]] || (cnt[p][q][a[i]] == cnt[p][q][a[ans]] && a[i] < a[ans]))
    			ans = i;
    	}
    	for(int i = l ; i <= R[p - 1] ; i++)--cnt[p][q][a[i]];//这两行用于清除开头,结尾的影响
    	for(int i = L[q + 1] ; i <= r ; i++)--cnt[p][q][a[i]];
    	
    	return c[ans];
    

    时空复杂度

    时间复杂度(前半段为预处理,后半段为每一次查询):

    [O(nt^2+mfrac{n}{t}) ]

    题目已经给出m,n在一个数量级(最大数据),因此,我们考虑让两边尽量平均,则有方程组

    [nt^2=mfrac{n}{t} ]

    解得t等于三次根号下m,约等于三次根号下n

    空间复杂度:

    [O(nt^2) ]

    由于t才去到30~40,所以是可以接受的

    代码(含对拍)

    声明:使用对拍文件时需要关掉强制在线,即直接输入l,r(详情参考std.cpp)

    第一次交时忘记了强制在线(听取WA声一片),曾一度怀疑人生:难道我暴力都写错了?

    不然就一次AC了

    话说样例真水啊,没开强制在线都能过

    AC代码(tested.cpp)

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #define nn 40010
    #define max_t 64+10
    using namespace std;
    int read() {
    	int re = 0;
    	bool sig = false;
    	char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-')sig = true;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return sig ? -re : re;
    }
    int n , m;
    int t , len;
    int a[nn];
    int c[nn]; 
    int zs[max_t][max_t];
    int L[max_t] , R[max_t];
    int pos[nn];
    unsigned short cnt[max_t][max_t][nn];
    
    struct node {
    	int dat , id;
    }b[nn];
    bool cmp(node a , node b){return a.dat < b.dat;}
    void Init() {
    	//离散化 
    	for(int i = 1 ; i <= n ; i++)
    		b[i].dat = a[i] , b[i].id = i;
    	sort(b + 1 , b + n + 1 , cmp);
    	int cnt_ = 0;
    	for(int i = 1 ; i <= n ; i++) {
    		if(b[i].dat != b[i - 1].dat) cnt_++;
    		a[b[i].id] = cnt_;
    	}
    	//分块 
    	t = 0;
    	while(t * t * t < n)++t;
    	len = n / t;
    	
    	L[1] = 1 , R[1] = len;
    	for(int i = 2 ; i <= t ; i++)
    		L[i] = R[i - 1] + 1,
    		R[i] = len * i;
    	if(R[t] < n)
    		++t , L[t] = R[t - 1] + 1 , R[t] = n;
    	
    	for(int i = 1 ; i <= t ; i++)
    		for(int j = L[i] ; j <= R[i] ; j++)
    			pos[j] = i;
    	//预处理cnt 和 众数 
    	for(int i = 1 ; i <= t ; i++)
    		for(int j = i ; j <= t ; j++)
    			for(int k = L[i] ; k <= R[j] ; k++) {
    				++cnt[i][j][a[k]];
    				if(cnt[i][j][a[k]] > cnt[i][j][a[zs[i][j]]] || (cnt[i][j][a[k]] == cnt[i][j][a[zs[i][j]]] && a[k] < a[zs[i][j]]))
    					zs[i][j] = k;
    			}
    }
    int tmp_cnt[nn];
    int query(int l , int r) {
    	int p = pos[l] , q = pos[r];
    	int ans = 0;
    	if(p == q) {
    		for(int i = l ; i <= r ; i++) {
    			++tmp_cnt[a[i]];
    			if(tmp_cnt[a[i]] > tmp_cnt[a[ans]] || (tmp_cnt[a[i]] == tmp_cnt[a[ans]] && a[i] < a[ans]))
    				ans = i;
    		}
    		for(int i = l ; i <= r ; i++)
    			--tmp_cnt[a[i]];
    		
    		return c[ans];
    	} 
    	++p , --q;
    	ans = zs[p][q];
    	for(int i = l ; i <= R[p - 1] ; i++) {
    		++cnt[p][q][a[i]];
    		if(cnt[p][q][a[i]] > cnt[p][q][a[ans]] || (cnt[p][q][a[i]] == cnt[p][q][a[ans]] && a[i] < a[ans]))
    			ans = i;
    	}
    	for(int i = L[q + 1] ; i <= r ; i++) {
    		++cnt[p][q][a[i]];
    		if(cnt[p][q][a[i]] > cnt[p][q][a[ans]] || (cnt[p][q][a[i]] == cnt[p][q][a[ans]] && a[i] < a[ans]))
    			ans = i;
    	}
    	for(int i = l ; i <= R[p - 1] ; i++)--cnt[p][q][a[i]];
    	for(int i = L[q + 1] ; i <= r ; i++)--cnt[p][q][a[i]];
    	
    	return c[ans];
    } 
    int main() {
    	n = read(),	m = read();
    	for(int i = 1 ; i <= n ; i++)
    		c[i] = a[i] = read();
    	Init();
    	
    	int lastans = 0;
    	for(int i = 1 ; i <= m ; i++) {
    		int l , r;
    		l = read(),	r = read();
    		l = ((l + lastans - 1) % n) + 1;
    		r = ((r + lastans - 1) % n) + 1;
    		if(l > r) {int tmp = l ; l = r ; r = tmp;}
    //		query(l , r);
    		printf("%d
    " , lastans = query(l , r));
    	}
    	return 0;
    } 
    

    暴力(std.cpp)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define nn 40010
    using namespace std;
    int read() {
    	int re = 0;
    	bool sig = false;
    	char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-')sig = true;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return sig ? -re : re;
    }
    
    int n , m ;
    int a[nn] , c[nn];
    struct node {
    	int dat , id;
    }b[nn];
    bool cmp(node a , node b){return a.dat < b.dat;}
    
    int cnt[nn];
    int main() {
    	n = read(),	m = read();
    	for(int i = 1 ; i <= n ; i++)
    		a[i] = b[i].dat = c[i] = read(),	b[i].id = i;
    	
    	sort(b + 1 , b + n + 1 , cmp);
    	int cnt_ = 0;
    	for(int i = 1 ; i <= n ; i++) {
    		if(b[i].dat != b[i - 1].dat) cnt_++;
    		a[b[i].id] = cnt_;
    	} 
    	
    	for(int i = 1 ; i <= m ; i++) {
    		int l =read() , r = read();
    		memset(cnt , 0 , sizeof(cnt));
    		int ans = 0;
    		for(int j = l ; j <= r ; j++) {
    			cnt[a[j]]++;
    			if(cnt[a[j]] > cnt[a[ans]] || (cnt[a[j]] == cnt[a[ans]] && a[j] < a[ans]))
    				ans = j;
    		}
    		printf("%d
    " , c[ans]);
    	}
    	return 0;
    }
    

    随机数据(random.cpp)

    #include <bits/stdc++.h>
    using namespace std;
    int random(int r , int l = 1) {
    	return (l == r ? l : ((long long)rand() * rand() % (r - l) + l ));
    }
    int main() {
    	srand((unsigned)time(0));
    	
    	int n = 40000 , m = 50000;
    	printf("%d %d
    " , n , m);
    	for(int i = 1 ; i <= n ; i++)
    		printf("%d " , random(1000000000));
    	putchar('
    ');
    	
    	for(int i = 1 ; i <= m ; i++) {
    		int l = random(n) , r = random(n , l);
    		printf("%d %d
    " , l , r); 
    	} 
    		
    	return 0;
    }
    

    对拍控制(compare.cpp)

    continue删掉,std的双斜杠去掉,即开启std和tested的对拍

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
    	
    	while(true) {
    		system("random.exe > input.txt");
    		puts("random");
    		
    //		system("std.exe < input.txt > output1.txt");
    //		puts("std");
    		int t = clock();
    		if(system("tested.exe < input.txt > output2.txt") != 0) {//检验运行时错误,记得return 0
    			cout << "RE";
    			return 0;
    		}
    		puts("tested");
    		printf(">time:%d
    " , clock() - t);
    		
    		continue;
    		if(system("fc output1.txt output2.txt")) {
    			cout << "WA";
    			system("start input.txt");
    			return 0;
    		}
    		
    	}
    	return 0;
    }
    
  • 相关阅读:
    如何设置Xcode模拟器地图的当前位置
    序列化框架MJExtension详解 + iOS ORM框架
    ios 容错处理JKDataHelper和AvoidCrash
    2017.5.3最新申请公司开发者账号整个流程(包括邓白氏申请详细流程带截图)
    详细分享UICollectionView的自定义布局(瀑布流, 线性, 圆形...)
    Mac系统安装和配置tomcat步骤详解
    Mac下intellij IDEA新建javaweb项目
    Apple Mach-O Linker Warning 警告解决办法
    项目适配iOS9遇到的一些问题及解决办法(更新两个小问题)
    iOS 中 延迟操作四种方式
  • 原文地址:https://www.cnblogs.com/dream1024/p/14038692.html
Copyright © 2020-2023  润新知