「USACO24DEC G」Cowdepenence 题解
「USACO24DEC G」Cowdepenence 题解
知识点
根号分治。
分析
\(O(n\sqrt{n}\log_2{n})\)
我们考虑先将每种颜色分开,然后对于每种颜色分别处理。
可以类比数论分块,我们会发现当距离范围 \(x\) 处于某个区间 \([l,r]\) 时,友谊小组的最小数量 \(cnt\) 的值是相同的,而且根据数论分块的结论,本质不同的 \(cnt\) 数量不会超过 \(\sqrt{n}\)。
显然 \(x,cnt\) 的关系满足单调性,所以我们可以直接暴力二分 \(x\) 的阙值,进行检验,并且对每次重复检验的 \(x\) 进行记忆化,然后差分统计答案。
unordered_map<int,int> rec;
void Solve(const Vi &vec) {
	for(int L(1),R(1),c(Check(vec,1)); L<=n; c=Check(vec,L=++R)) {
		for(int l(L+1),r(n),mid((l+r)>>1); l<=r; mid=(l+r)>>1)
			Check(vec,mid)>=c?R=mid,l=mid+1:r=mid-1;
		ans[L]+=c,ans[R+1]-=c;
	}
	rec.clear();
}
假设我们现在处理的颜色有 \(k\) 个,那么我们有两种检验方法:
- 
\(O(k)\):
直接一遍扫过去。
int Check1(const Vi &vec,const int &lim) { int cnt(0),sta(0); for(const int &x:vec)if(!sta||x-sta>lim)++cnt,sta=x; return cnt; } - 
\(O(\frac{n}{x}\log_2{k})\):
我们中间不断二分下一个点。
int Check2(const Vi &vec,const int &lim) { int cnt(0),sta(vec.front()); while(true) { ++cnt; int it(upper_bound(vec.begin(),vec.end(),sta+lim)-vec.begin()); if(it==(int)vec.size())break; sta=vec[it]; } return cnt; } 
我们每次取其中复杂度小的一个。
int Check(const Vi &vec,const int &lim) {
	if(rec.count(lim))return rec[lim];
	auto cal=[&]() -> ll { return (ll)rec.size()*lim/n; };
	return rec[lim]=(cal()<20&&(1<<cal())<(int)rec.size()?Check1(vec,lim):Check2(vec,lim));
}
时间复杂度 \(O(n\sqrt{n}\log_2{n})\)。
int n;
int a[N],b[N],ans[N];
Vi vec[N];
int Check1(const Vi &vec,const int &lim) {
	int cnt(0),sta(0);
	for(const int &x:vec)if(!sta||x-sta>lim)++cnt,sta=x;
	return cnt;
}
int Check2(const Vi &vec,const int &lim) {
	int cnt(0),sta(vec.front());
	while(true) {
		++cnt;
		int it(upper_bound(vec.begin(),vec.end(),sta+lim)-vec.begin());
		if(it==(int)vec.size())break;
		sta=vec[it];
	}
	return cnt;
}
unordered_map<int,int> rec;
int Check(const Vi &vec,const int &lim) {
	if(rec.count(lim))return rec[lim];
	auto cal=[&]() -> ll { return (ll)vec.size()*lim/n; };
	return rec[lim]=(cal()<20&&(1<<cal())<(int)vec.size()?Check1(vec,lim):Check2(vec,lim));
}
void Solve_Min(const Vi &vec) {
	for(int L(1),R(1),c(Check(vec,1)); L<=n; c=Check(vec,L=++R)) {
		for(int l(L+1),r(n),mid((l+r)>>1); l<=r; mid=(l+r)>>1)
			Check(vec,mid)>=c?R=mid,l=mid+1:r=mid-1;
		ans[L]+=c,ans[R+1]-=c;
	}
	rec.clear();
}
signed main() {
#ifdef Plus_Cat
	freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
	I(n);
	FOR(i,1,n)I(a[i]),b[i]=a[i];
	sort(b+1,b+n+1),b[0]=unique(b+1,b+n+1)-b-1;
	FOR(i,1,n)a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b;
	FOR(i,1,n)vec[a[i]].push_back(i);
	FOR(i,1,b[0])Solve_Min(vec[i]);
	FOR(i,1,n)O(ans[i]+=ans[i-1],'\n');
	return 0;
}
\(O(n\sqrt{n\log_2{n}})\)
发现其实相同颜色中 \(x\le \sqrt{n}\) 的部分,直接暴力求解反而更快,这部分复杂度变成了 \(O(n\sqrt{n})\)。
进一步调整块长,发现如果 \(x\le \sqrt{n\log_2{n}}\) 的部分都暴力求解,复杂度就变成了 \(O(n\sqrt{n\log_2{n}})\)。
void Solve(const Vi &vec) {
	FOR(i,1,Bl) {
		int tmp(Check1(vec,i));
		ans[i]+=tmp,ans[i+1]-=tmp;
	}
	for(int L(Bl+1),R(Bl+1),c(Check(vec,Bl+1)); L<=n; c=Check(vec,L=++R)) {
		for(int l(L+1),r(n),mid((l+r)>>1); l<=r; mid=(l+r)>>1)
			Check(vec,mid)>=c?R=mid,l=mid+1:r=mid-1;
		ans[L]+=c,ans[R+1]-=c;
	}
	rec.clear();
}
signed main() {
#ifdef Plus_Cat
	freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
	I(n),Bl=ceil(sqrt(n*log2(n)));
	FOR(i,1,n)I(a[i]),b[i]=a[i];
	sort(b+1,b+n+1),b[0]=unique(b+1,b+n+1)-b-1;
	FOR(i,1,n)a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b;
	FOR(i,1,n)vec[a[i]].push_back(i);
	FOR(i,1,b[0])Solve(vec[i]);
	FOR(i,1,n)O(ans[i]+=ans[i-1],'\n');
	return 0;
}
\(O(n\sqrt{n})\)
要求一个整数序列 \((a_1,a_2,\ldots,a_n)\)。已知:
- 它是非递增的,即 \(a_k \ge a_{k+1}\)。
 - \(0 \le a_k \le \frac{n}{k}\)。
 可以在 \(O(C)\) 时间内得到 \(a_k\) 的一个值,则可以在 \(O(n+\sqrt{n}C)\) 的复杂度内求出整个序列。
我们仿照上面那篇博文分析一下复杂度:
令 \(a_0=n+1,a_{n+1}=0\),然后开始分治:
假设现在分治区间为 \([l,r]\),则:
- 如果 \(a_{l-1}=a_{r+1}\),则全部赋为同值。
 - 否则求出 \(a_{mid}\),然后递归 \([l,mid),(mid,r]\)。
 
那么对于深度为 \(d\) 的一层,求值操作数量是 \(O(2^{\frac{d}{2}})\) 级别的。
所以总复杂度为:
即 \(O(\sqrt{n})\)。那么我们也这么干就可以了。
//#define Plus_Cat ""
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Vi vector<int>
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define EDGE(g,i,x,y) for(int i=(g).h[(x)],y=(g)[(i)].v;~i;y=(g)[(i=(g)[i].nxt)>0?i:0].v)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);return Main();}signed Main
using namespace std;
constexpr int N(1e5+10);
namespace IOEcat {
	//...
} using namespace IOEcat;
int n,Bl;
int a[N],b[N],ans[N];
Vi vec[N];
int Check1(const Vi &vec,const int &lim) {
	int cnt(0),sta(0);
	for(const int &x:vec)if(!sta||x-sta>lim)++cnt,sta=x;
	return cnt;
}
int Check2(const Vi &vec,const int &lim) {
	int cnt(0),sta(vec.front());
	while(true) {
		++cnt;
		int it(upper_bound(vec.begin(),vec.end(),sta+lim)-vec.begin());
		if(it==(int)vec.size())break;
		sta=vec[it];
	}
	return cnt;
}
int Check(const Vi &vec,const int &lim) {
	auto cal=[&]() -> ll { return (ll)vec.size()*lim/n; };
	return cal()<20&&(1<<cal())<(int)vec.size()?Check1(vec,lim):Check2(vec,lim);
}
void Sep(int l,int r,int vl,int vr,const Vi &vec) {
#define mid ((l+r)>>1)
	if(l>r)return;
	if(vl==vr)return ans[l]+=vl,ans[r+1]-=vr,void();
	int vm(Check(vec,mid));
	ans[mid]+=vm,ans[mid+1]-=vm,Sep(l,mid-1,vl,vm,vec),Sep(mid+1,r,vm,vr,vec);
#undef mid
}
signed main() {
#ifdef Plus_Cat
	freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
	I(n),Bl=ceil(sqrt(n*log2(n)));
	FOR(i,1,n)I(a[i]),b[i]=a[i];
	sort(b+1,b+n+1),b[0]=unique(b+1,b+n+1)-b-1;
	FOR(i,1,n)a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b;
	FOR(i,1,n)vec[a[i]].push_back(i);
	FOR(i,1,b[0])Sep(1,n,n,1,vec[i]);
	FOR(i,1,n)O(ans[i]+=ans[i-1],'\n');
	return 0;
}

                
            
        
浙公网安备 33010602011771号