A-签到题

注意到,,故

对任意的,令,则,此时

#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)printf("%d ",i);
	return 0;
}

B-Bob的蛋糕店

设Alice拿走的蛋糕分别是第个,根据质心公式有,直接模拟即可。

复杂度为

注意:为了避免精度问题,判断时,交叉相乘再相减等于零即可。

#include<bits/stdc++.h>
using namespace std;
int n,k,a[25],A,B,fl,q[25];
void dfs(int deep,int x){
	if(deep==k+1){
		int sum=0;
		for(int i=1;i<=k;i++)
			sum+=B*a[q[i]]*q[i]-A*a[q[i]];
		if(sum==0)fl=1;
		return ;
	}
	for(int i=x;i<=n+deep-k;i++){
		q[deep]=i;
        dfs(deep+1,i+1);
	}
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i],A+=a[i]*i,B+=a[i];
	dfs(1,1);
    if(fl)cout<<"Yes";
    else cout<<"No";
	return 0;
}

C-修复排列

题目核心需求

给定两个排列 (初始排列)和 (修复用排列),每次破坏会将 位置的 变为。需通过 “用 替换 ” 的操作,将 恢复为 的排列,求第 次破坏后最少的操作次数(每次答案独立计算)。

核心思路

要恢复排列,关键是解决 “修复值重复” 问题: 破坏位置 必须修复( 不能在排列中),修复值为

可能在原 的未破坏位置存在(比如 ,原 未破坏,会重复);

需追踪这种 “重复冲突链”:修复一个位置后,若其修复值在原 中对应位置未处理,需继续修复该位置,直到无新冲突。

代码通过以下工具实现该逻辑:

:记录值 在原排列 中的下标(因 是排列,每个 唯一对应一个下标);

:记录 “必须修复的值”(每个值对应唯一位置, 的大小即为最少操作次数,避免重复处理冲突链)。

代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
#define fixed(x) fixed<<setprecision(x)
const int N=2e5+5;
int main(){
   int n;cin>>n;
   vector<int>p(n+1),id(n+1),d(n+1);
   for(int i=1;i<=n;i++)cin>>p[i];
   for(int i=1;i<=n;i++)cin>>id[i];
   for(int i=1;i<=n;i++)cin>>d[i];
   map<int,int>mp;
   for(int i=1;i<=n;i++)mp[p[i]]=i;
   set<int>st;
   for(int i=1;i<=n;i++){
   	int x=d[id[i]];
   	while(st.find(x)==st.end()){
   		st.insert(x);
   		x=d[mp[x]];
   	}
   	cout<<st.size()<<' ';
   }
   return 0;
}

D-图G

根据的性质,当且仅当时,点和点连有一条边。

即:

表示任意的,满足 的个数,当 时,跟点连有边的点个。

复杂度为

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1005;
int n,a[N],b[N];
ll ans,f[M][M];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)scanf("%d",&b[i]);
	for(int i=1;i<=n;i++){
		if(a[i]%b[i]==0){
			a[i]/=b[i];
			for(int j=1;j<=1000;j++){
				if(a[i]%j==0){
					ans+=f[j][b[i]];
					f[b[i]][j]++;
				}
			}
		}
	}
	printf("%lld",ans);
	return 0;
} 

E-小R的min

根据的定义,易得:对于任意的,将按升序排序后,当为中位数时,最小。记,故求第小的数

对于每次询问,我们可以考虑二分中位数,找到最小的满足

虽然不好计算,但是注意到的单调性,我们可以考虑容斥,即:.

对于任意的,我们可以先预处理出.

,根据的单调性,还表示最大的满足,故可以二分,在内计算出,而的前缀和。因为是中位数,而且必定是数组中的数,所以只需要预处理数组中的数即可,离散化后总复杂度为

同理,我们也可以预处理

最后,当时,对于任意的,有,故;当时,对于任意的,有,故

复杂度为

不过,内测的时候发现常数较小的做法刚好可以通过,赛时发现许多选手也通过了...

 #include <bits/stdc++.h>
using namespace std;
const int N=1005;
int n,m,a[N],f[N][N],sl[N][N],sr[N][N],b[N][N];
bool check(int l,int r,int x,int k){
	int sum=0;
	if(b[r][x]>=l)sum=sl[r][x]+sr[l][x]+(l-1)*(n-r)-sl[n][x];
	return sum>=k;
}
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		int mi=a[i];
		for(int j=i;j<=n;j++){
			mi=min(mi,a[j]);
			f[i][j]=mi;
		}
	}
	sort(a+1,a+n+1);
	int len=unique(a+1,a+n+1)-a-1;
	for(int i=1;i<=len;i++) {
		for(int j=1;j<=n;j++){
			int l=0,r=j,mid;
			while(l<r){
				mid=(l+r+1)>>1;
				if(f[mid][j]<=a[i])l=mid;
				else r=mid-1;
			}
			sl[j][i]=sl[j-1][i]+l;
			b[j][i]=l;
		}
		for(int j=n;j;j--){
			int l=j,r=n+1,mid;
			while(l<r){
				mid=(l+r)>>1;
				if(f[j][mid]<=a[i])r=mid;
				else l=mid+1;
			}
			sr[j][i]=sr[j+1][i]+n+1-l;
		}
	}
	int L,R,k;
	while(m--){
		scanf("%d%d",&L,&R);
		k=((R-L+1)*(R-L+2)/2+1)/2;
		int l=1,r=len,mid;
		while(l<r){
			mid=(l+r)>>1;
			if(check(L,R,mid,k))r=mid;
			else l=mid+1;
		}
		printf("%d\n",a[l]);
	}
	return 0;
}


F-种树

首先,我们先证明一个结论:

,其中

即求:

接着, 对该式子进行莫比乌斯反演:

最后,我们可以用杜教筛计算出结果:

因为

所以

复杂度为

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+80,mod=1e9+7;
const ll inv3=333333336,inv2=500000004;
ll n,mu[N],p[N],M=1e6;
bool flg[N];
ll sum(ll x){
	return x%mod*(x%mod+1)%mod*inv2%mod;
}
ll sum2(ll x){
	x%=mod;
	return x*(x+1)%mod*(x*2%mod+1)%mod*inv2%mod*inv3%mod;
}
void init(){
	int top=0;
	mu[1]=1;
	for(int i=2;i<=M;i++){
		if(!flg[i]){
			p[++top]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=top&&p[j]*i<=M;j++){
			flg[p[j]*i]=true;
			if(i%p[j]==0){
				mu[i*p[j]]=0;
				break;
			}
			mu[i*p[j]]=-mu[i];
		}
	}
	for(ll i=1;i<=M;i++)mu[i]=(mu[i]*i%mod*i%mod+mu[i-1])%mod;
}
map<ll,ll> mp;
ll SF(ll x){
	if(x<=M)return mu[x];
	if(mp[x])return mp[x];
	ll ans=1;
	for(ll l=2,r;l<=x;l=r+1){
		r=x/(x/l);
		ans=(ans-(sum2(r)-sum2(l-1))%mod*SF(x/l)%mod)%mod;
	}
	return mp[x]=(ans+mod)%mod;
}
int main() {
	scanf("%lld",&n);
	M=min(M,n),init();
	ll ans=0;
	for(ll l=1,r;l<=n;l=r+1){
		ll x=n/l,y=sum(x);
		r=n/x;
		ans=(ans+y*y%mod*(SF(r)-SF(l-1))%mod)%mod;
	}
	printf("%lld",(ans+mod)%mod);
	return 0;
}