Codeforces Round #813(Div.2) 题解

比赛链接

ABC 送分题,B的证明好像有点有趣,不过结论猜就完了
A

// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;

const int inf = 1e9;

int n,k;
int a[105];

void solve(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	int p = k, cnt=0;
	for(int i=1;i<=k;i++){
		if(a[i] > p)++ cnt;
	}
	printf("%d\n",cnt);
}

signed main(){
	int te;scanf("%d",&te);
	while(te --)solve();

	return 0;
}


B

// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;

const int inf = 1e9;

void solve(){
	int n;
	scanf("%d",&n);
	if(n%2==0){
		for(int i=1;i<=n;i+=2)printf("%d %d ",i+1,i);
		puts("");
	}else{
		printf("1 ");
		for(int i=2;i<=n;i+=2)printf("%d %d ",i+1,i);
		puts("");
	}
}

signed main(){
	int te;scanf("%d",&te);
	while(te--)solve();

	return 0;
}


C

// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;

const int inf = 1e9;

int n;
int a[100005],lt[100005],vis[100005],mx[100005];

void solve(){
	memset(lt,0,sizeof lt);
	memset(vis,0,sizeof vis);
	memset(mx,0,sizeof mx);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	int lst=-1;
	for(int i=n;i>=2;i--){
		if(a[i] < a[i-1]){lst = i-1;break;}
	}
	for(int i=1;i<=n;i++)lt[a[i]] = i;
	for(int i=1;i<=n;i++)mx[i] = max(mx[i-1],lt[a[i]]);
	if(lst == -1){puts("0");return ;}
	for(int i=lst;i<=n;i++){
		if(lt[a[i]]!=i)continue;
		if(mx[i] == i){
			int cnt=0;
			for(int j=1;j<=i;j++){
				if(!vis[a[j]])++cnt,vis[a[j]]=1;
			}
			printf("%d\n",cnt);
			return ;
		}
	}
}

signed main(){
	int te;scanf("%d",&te);
	while(te --)solve();

	return 0;
}

D
首先注意到对于一个给定序列\(a_1..a_n\),答案就是

\[max(2*\min_{i=1}^na_i, \max_{i=1}^{n-1}\min({a_i,a_{i+1}})) \]

解释:首先我们要最大化的就是某两点最短路的最大值。考虑最小值\(a_x\)的位置,如果\(x\)位于\([l,r]\)之间,那么根据定义,\(l\)\(r\)之间连一条长为最小值的边,易知这一定是l和r的最短路长度,显然不是我们想要的,因为我们要的是最短路的最大值。如果\(x \notin [l..r]\),我们考虑l和r的最短路长什么样,考虑两种情况:$l \rightarrow p\rightarrow r $ 显然我们取能包含住\(x\)\([p..l]\&[p..r]\)就能取到最小值,即为\(2*a_x\),显然这是这种情况最小值,第二种情况是直接\(l\rightarrow r\),长度也很显然,是\(\min{a[l..r]}\),最短距离即为二者最小值

现在我们想要最大化这个最小值,怎么做?显然第一种情况不可能再小了,只能第二种情况变大,注意到\(\min a[l..r] \leq \min a[l+1..r]\),也就是说我们应该尽量缩小l r距离,显然当\(r=l+1\)可以。也就是得到了上式

如何利用好赋值?普通的贪心是会有反例的,但是我们发现,对于某一个答案来说,如果他能够通过\(k\)次赋值得到,那么比这个答案更小的一定也能不多于\(k\)次赋值得到,可以二分。

于是二分答案,判断修改次数是否小于\(k\)即可
(考场尝试了3次贪心都错了...)

// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;

const int inf = 1e9,maxn=100005;

int n,k;
int a[maxn],b[maxn];

int check(int x){
	for(int i=1;i<=n;i++)b[i]=a[i];
	int tot=0;
	for(int i=1;i<=n;i++)
		if(a[i]*2 < x)++ tot, b[i] = 1e9;
	int res=2;
	for(int i=1;i<n;i++){
		res = min(res, (b[i]<x) + (b[i+1]<x));
	}
	return res+tot <= k;
}

void solve(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	int l=1,r=1e9,mid,ans;
	while(l <= r){
		mid = l+r>>1;
		if(check(mid))l = mid+1,ans=mid;
		else r = mid-1;
	}
	printf("%d\n",ans);
}

int main(){
	int te;scanf("%d",&te);
	while(te --)solve();
	return 0;
}

E1&E2
容斥一下,就转化为了求\(lcm(i,j,k)<i+j+k\)的方案
转化的目的是为了放缩
\(lcm(i,j,k)<i+j+k<3k\),又因为是\(k\)的倍数,必然有\(lcm(i,j,k)=k\) 或者 \(lcm(i,j,k)=2k且i+j>k\)(i+j>k是由2k<i+j+k得到)
先考虑第一种情况,显然\(i\)\(j\)都是\(k\)的约数,对于每个\([l..r]\)我们可以暴力枚举\(k\),统计在\([l,k)\)\(k\)的约数个数,记为\(cnt\),则其对答案的贡献即为\(\binom{cnt}{2}\)

第二种情况?首先\(i\)\(j\)都是\(2k\)的约数,易知\(j\geq\)\(\frac{k}{2}\),所以\(j\)只可能取\(\frac{2k}{3}\)(如果为整数),将\(j\)代回不等式反解,\(\frac{k}{3}<i<\frac{2k}{3}\)\(i\)可取\(\frac{2k}{5}和\frac{k}{2}\),如果为整数,对于每个\(k\)额外判断一下即可
求出答案之后\(C_{r-l+1}^3-方案\)即可
时间复杂度\(O(Tnlogn)\),可以过E1

对于\(T\leq 1e5\),考虑离线,按照\(l\)从大到小排序
维护一个指针,从大到小扫描,对于某个位置\(cur\),考虑\(cur*2, cur*3, ...\)(相当于E1我是枚举因数,E2我是枚举倍数,对于每个因数只需要枚举一次)每次枚举到某个倍数\(m*cur\),第一次+0,第二次+1,...这样就相当于统计了E1中的\(\binom{cnt}{2}\)(为什么第二次才+1?因为\(m*cur\)相当于枚举的是\(k\),而枚举到\(k\)的次数就相当于在[l..k)间\(k\)的因子个数cnt,第二次枚举到相当于有2个这种因数,\(C_2^2=1, C_3^2=3=1+2, ...\)
查询的时候就相当于\([1..r]\)的区间和,因为我\(l\)从大到小的,所以不存在不合法情况
树状数组维护即可

考虑对于\([l..r]\)第二种情况的影响,对于\(2/5k, 2/3k, k\)的形式,\(k\)必然为15的倍数,有约束\(k\leq r且2/5k \leq l-1(化归为k\leq\frac{5*(l-1)}{2})\),对于\(k\leq m\),k是15的倍数,k的个数为\(m/15\),根据这个统计答案,对于\(1/2k, 2/3k, k\)同理

E1

// by Balloons
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;

const int inf = 1e9,maxn=2e5+5;
vector<int>divsor[maxn];

void solve(){
	int l,r;scanf("%d%d",&l,&r);
	LL res=0;
	for(int i=l+2;i<=r;i++){
		int k = i,cnt=0;
		for(int tp=0;tp<divsor[k].size();tp++){
			int cur = divsor[k][tp];
			if(l<=cur && cur<k)++ cnt;
		}
		res += 1ll*cnt*(cnt-1)/2;
		if(k%15==0 && 2*k/5 >=l)++ res;
		if(k%6 == 0 && k/2 >=l) ++ res;
	}
	int len = r-l+1;
	LL ans = 1ll*len*(len-1)*(len-2) / 6;
	printf("%I64d\n",ans - res);
}

signed main(){
	for(int i=1;i<=200000;i++){
		for(int j=i;j<=200000;j+=i){
			divsor[j].push_back(i);
		}
	}
	int te;scanf("%d",&te);
	while(te--)solve();

	return 0;
}

E2

// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;

const int inf = 1e9,maxn=2e5+5;

struct node{int l,r,id;}a[maxn];
int cmp(node a,node b){return a.l > b.l;}
int buc[maxn],bit[maxn];
LL qu[maxn];

int lowbit(int x){return x&(-x);}
void add(int x,int tp){for(int i=x;i<=200000;i+=lowbit(i))bit[i] += tp;}
LL query(int x){LL res=0;for(int i=x;i;i-=lowbit(i))res += bit[i];return res;}

signed main(){
	int T;scanf("%d",&T);
	for(int i=1;i<=T;i++){
		scanf("%d%d",&a[i].l,&a[i].r);	
		a[i].id = i;
	}
	sort(a+1,a+T+1,cmp);
	
	int curid = 200001;
	for(int i=1;i<=T;i++){
		int lx = a[i].l, ly = a[i].r, lid = a[i].id;
		while(curid>lx){
			-- curid;
			for(int t=curid*2;t<=200000;t+=curid){
				add(t,buc[t]);
				++ buc[t];
			}
		}
		int len = ly-lx+1;
		LL ans = 1ll*len*(len-1)*(len-2)/6;
		ans -= query(ly);
		ans -= max(0, ly/15 - (lx-1) / 6);
		ans -= max(0ll, ly/6 - 1ll*(lx-1) / 3); 
		qu[lid] = ans;
	}
	for(int i=1;i<=T;i++)printf("%I64d\n",qu[i]);

	return 0;
}


posted @ 2022-08-14 17:36  SkyRainWind  阅读(84)  评论(0编辑  收藏  举报