CF1179E Alesya and Discrete Math(分治,随机化)

这是一篇关于利用分治和随机化算法解决编程竞赛题目CF1179E的文章。文章指出,由于边界条件的限制,直接调试非常困难。题目要求在2e5次操作内,找到一组划分满足特定条件。作者提出了一种分治策略,首先找到使得函数值等于2L的点,然后递归地缩小问题规模。通过随机选择函数并调整范围,期望的递归次数为O(logn)。最终,计算了算法的复杂度,并强调了优化常数和调试的重要性。
摘要由CSDN通过智能技术生成

题目
边界调到死,输入输出交互对调试端及其不友好

题意:
n ( ≤ 1 e 3 ) n(\leq1e3) n(1e3)个值域在 [ 0 , L ] [0,L] [0,L]的函数满足 ∀ i ∈ [ 1 , n ] , x ∈ [ 1 , 1 e 18 ] , f i ( 0 ) = 0 , f i ( 1 e 18 ) = L , f i ( x ) − f i ( x − 1 ) ∈ [ 0 , 1 ] \forall i\in [1,n],x\in[1,1e18] ,f_i(0)=0,f_i(1e18)=L,f_i(x)-f_i(x-1) \in[0,1] i[1,n],x[1,1e18],fi(0)=0,fi(1e18)=L,fi(x)fi(x1)[0,1],每次操作可以询问一个函数在一个点的值,在 2 e 5 2e5 2e5次操作内求出一组划分 l i , r i l_i,r_i li,ri,满足 ∀ i ≠ j , [ l i , r i ] ∩ [ l j , r j ] \forall i\neq j ,[l_i,r_i] \cap [l_j,r_j] i=j[li,ri][lj,rj]是一个端点或空,且 ∀ i ∈ [ 1 , n ] f i ( r i ) − f i ( l i ) ≥ L n \forall i\in [1,n] f_{i}(r_i) - f_i(l_i) \geq \frac Ln i[1,n]fi(ri)fi(li)nL

题解:
考虑一个分治算法:
对所有函数都求出 x i x_i xi使得 f i ( x i ) = L 2 f_i(x_i) = \frac L2 fi(xi)=2L
那么按 x i x_i xi排序后,前一半和后一半就可以递归下去,取值范围从 [ 0 , 1 e 18 ] [0,1e18] [0,1e18]变成了 [ 0 , x n 2 ] [0,x_{\frac n2}] [0,x2n] [ x n 2 , 1 e 18 ] [x_{\frac n2},1e18] [x2n,1e18]
求出 x i x_i xi的操作数复杂度是 O ( log ⁡ 1 e 18 ) O(\log 1e18) O(log1e18)
所以总操作数是 O ( n log ⁡ 1 e 18 log ⁡ n ) O(n \log 1e18 \log n) O(nlog1e18logn)

考虑用其他方法求出 x n 2 x_{\frac n2} x2n
随机一个函数 f k ( x ) f_k(x) fk(x),求出 x k x_k xk使得 f k ( x k ) = L 2 f_k(x_k) = \frac L2 fk(xk)=2L
对于其他函数求出 f i ( x k ) f_i(x_k) fi(xk),如果 f i ( x k ) < f k ( x k ) f_i(x_k) < f_k(x_k) fi(xk)<fk(xk)的大于了 n 2 \frac n2 2n
那么说明 f k f_k fk肯定不是中位函数,中位函数在 f i ( x k ) < f k ( x k ) f_i(x_k) < f_k(x_k) fi(xk)<fk(xk) f i f_i fi中。
然后递归缩小范围后再随机函数再算。
期望递归 O ( log ⁡ n ) O(\log n) O(logn)次。
所以对于大小为 n n n的一层的复杂度为 O ( n log ⁡ 1 e 18 log ⁡ n + n ) O(n\log 1e18 \log n + n) O(nlog1e18logn+n)
求个和
∑ i = 1 10 2 10 − i log ⁡ 1 e 18 i + 2 i \sum_{i=1}^{10} 2^{10-i} \log 1e18 i + 2^i i=110210ilog1e18i+2i
= log ⁡ 1 e 18 n + n log ⁡ n = \log 1e18 n + n\log n =log1e18n+nlogn
需要卡卡常。
需要调到疯
A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define LL long long
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
#define maxn 1005
int n;LL L,al[maxn],ar[maxn];
int arr[maxn][maxn]={
{}
,{0,0,1,1,2,3,4,4}
,{0,1,1,2,2,3,3,4}
,{0,1,2,2,2,3,3,4}
,{0,0,0,0,1,2,3,4}
};

LL getv(int u,LL v){
	printf("? %d %lld\n",u,v);
	fflush(stdout);
	LL r;
	scanf("%lld",&r);
	return r/* = arr[u][v]*/;
}

LL ser(int u,LL v,LL l,LL r){
	LL m;
	for(;l<r;){
		m = l+r>>1;
		LL t = getv(u,m);
		if(t < v) l = m + 1;
		else if(t > v) r = m - 1;
		else return m;
	}
	return l;
}

int vis[maxn],tim;
LL llc;
void qry(vector<int>&g,vector<int>&p,LL ql,LL qr,LL aim,int ls){
	int t = g[rand() % g.size()];
	LL x = ser(t,aim,ql,qr);
	vector<int>l,r,z;
	rep(i,0,p.size()-1) if(p[i] != t){
		LL y = getv(p[i],x);
		if(y == aim) z.push_back(p[i]);
		if(y < aim)	l.push_back(p[i]);
		if(y > aim) r.push_back(p[i]);
	}
	for(;l.size() < ls && !z.empty();)
		l.push_back(z.back()),z.pop_back();
	for(;!z.empty();)
		r.push_back(z.back()),z.pop_back();
	if(l.size() <= ls && r.size() < p.size()-ls){
		g.clear();
		g.push_back(t);
		llc = x;
		return;
	}
//	printf("@%d %d %d\n",l.size(),r.size(),t);
	if(l.size() > ls){
		++tim;
		rep(i,0,l.size()-1) vis[l[i]] = tim;
		for(int i=0;i<g.size();i++) if(vis[g[i]] != tim)
			swap(g[i],g.back()),g.pop_back(),i--;
		qry(g,p,ql,qr,aim,ls);
	}
	else{
		++tim;
		rep(i,0,r.size()-1) vis[r[i]] = tim;
		for(int i=0;i<g.size();i++) if(vis[g[i]] != tim)
			swap(g[i],g.back()),g.pop_back(),i--;
		qry(g,p,ql,qr,aim,ls);
	}
}

void Solve(vector<int>&p,LL ql,LL qr,LL pl,LL pr){
//	printf("@%d %lld %lld %lld %lld\n",p.size(),ql,qr,pl,pr);
//	rep(i,0,p.size()-1) printf("%d%c",p[i]," \n"[i==p.size()-1]);
	if(p.empty()) return;
	if(p.size() == 1){
		al[p[0]] = ql , ar[p[0]] = qr;
		return;
	}
	vector<int>g = p;
	int m = p.size() / 2;
	qry(g,p,ql,qr,pl + (pr-pl) / p.size() * m,m);
	int t = g[0];
//	printf("##%d %lld\n",t,llc);
	vector<int>l,r,z;
	rep(i,0,p.size()-1){
		LL y = getv(p[i],llc);
		if(y == pl + (pr-pl) / p.size() * m) z.push_back(p[i]);
		if(y > pl + (pr-pl) / p.size() * m) l.push_back(p[i]);
		if(y < pl + (pr-pl) / p.size() * m) r.push_back(p[i]);
	}
	for(;l.size() < m;)
		l.push_back(z.back()),z.pop_back();
	for(;!z.empty();)
		r.push_back(z.back()),z.pop_back();
	LL tmp = llc;
	Solve(l,ql,tmp,pl,pl + (pr-pl) / p.size() * m);
	Solve(r,tmp,qr,pl + (pr-pl) / p.size() * m,pr);
}

int main(){
	scanf("%d%lld",&n,&L);
	vector<int>p;
	rep(i,1,n) p.push_back(i);
	Solve(p,0,1e18,0,L);
	puts("!");
	rep(i,1,n) printf("%lld %lld\n",al[i],ar[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值