牛客 小白月赛117 20250602

牛客 小白月赛117 20250602

https://ac.nowcoder.com/acm/contest/111309

A:
题目大意:

void solve(){
	int n;
	cin>>n;
	map<char,int> mp;
	for (int i=1;i<=n;i++){
		char c;
		cin>>c;
		mp[c]++;
	}
	for (char i='A';i<='Z';i++){
		if (mp[i]!=0&&mp[i+'a'-'A']==0||mp[i]==0&&mp[i+'a'-'A']!=0){
			cout<<"NO";
			return ;
		}
	}
	cout<<"YES";
}

map 记录字符出现次数

B:

题目大意:

int a[100010];

void solve(){
	int n;
	cin>>n;
	map<int,int> mp;
	for (int i=1;i<=n;i++){
		cin>>a[i];
		mp[a[i]]++;
	}
	if (mp[-1]==0&&mp[1]!=0 || mp[-1]!=0&&mp[1]==0){
		cout<<"NO";
	else
		cout<<"YES";
}

如果存在小于平均值的元素,那么一定至少存在一个大于平均值的元素

C:
题目大意:

void solve(){
	int q;
	cin>>q;
	while (q--){
		LL l,r;
		cin>>l>>r;
        if (r==l){
            cout<<-1<<endl;
            continue;
        }
		if (r-l+1==2){
			if (l==1) cout<<0<<endl;
			else cout<<-1<<endl;
			continue;
		}        
        if ((r-l+1)%2==1)
			cout<<1<<endl;
		else
			cout<<0<<endl;
	}
}

奇数和偶数的最大公约数必然为 \(1\),偶数和偶数的最大公约数一定大于 \(1\)

对于给定的区间 \([l,r]\) 其中的数可以被分为奇数和偶数两类

当奇数等于偶数时,奇数集构成 \(\mathbb{S}_1\) 偶数集构成 \(\mathbb{S}_2\) ,那么 \(\lvert {\rm{len}}(\mathbb{S}_1)-{\rm{len}}(\mathbb{S}_2)\rvert=0\)

当奇数和偶数个数差为 \(1\) (连续的序列个数差至多为 \(1\)),\(\lvert {\rm{len}}(\mathbb{S}_1)-{\rm{len}}(\mathbb{S}_2)\rvert=1\)

特别的,当 \(r=l+1\) 时两个端点值分别形成集合,当且仅当 \(l=1\) 时满足 \({\rm{gcd}}(l)=1\) 存在解

D:
题目大意:

set<pair<int,int>> st;
int a[1010];
int mp[1000010];

void add(int val){
	st.erase({mp[val],val});
	mp[val]++;
	st.insert({mp[val],val});
}

void del(int val){
	st.erase({mp[val],val});
	mp[val]--;
	if (mp[val])
		st.insert({mp[val],val});
}

void solve(){
	int n;
	cin>>n;
	for (int i=1;i<=n;i++) cin>>a[i];
	for (int i=1;i<=n;i++){
		mp[a[i]]++;
		st.insert({mp[a[i]],a[i]});
	}
	set<int> ans;
	for (int i=1;i<=n;i++){
		for (int j=1;j<=n;j++){
			if (i==j) continue;
			del(a[i]);
			add(a[i]+1);
			del(a[j]);
			add(a[j]-1);
			ans.insert(st.rbegin()->second);
			del(a[i]+1);
			add(a[i]);
			del(a[j]-1);
			add(a[j]);
		}
	}
	for (auto i:ans) cout<<i<<" ";
}

利用 set 维护众数,时间复杂度为 \(O(\log n)\) ,两层循环枚举不同的位置 \(i,j\) 时间复杂度为 \(O(n^2)\) ,在 \(n\le 1e3\) 的条件下可以通过

set<pair<int,int>> st;

set 按照 pair<int,int> 做小根堆排序,先参考 first 再参考 second,与众数的判断条件相同

所以对枚举的下标模拟删除和插入操作即可

void add(int val){
	st.erase({mp[val],val});
	mp[val]++;
	st.insert({mp[val],val});
}

set 中插入值为 \(val\) 的元素一个,需要先将原来值为 \(val\) 的元素删去,再插入 \(val\) 的元素个数加一

如果在原来的 set 内直接更改会破坏 set 的有序性

void del(int val){
	st.erase({mp[val],val});
	mp[val]--;
	if (mp[val])
		st.insert({mp[val],val});
}

删去 set 内值为 \(val\) 的元素一个,仍然需要删去先前的元素,如果该元素在有且仅有一个,那么就不需要再插入了

ans.insert(st.rbegin()->second);//记录答案

E:

题目大意:

void solve(){
	int n;
	cin>>n;
	set<LL> st;
	for (int i=1;i<=n;i++){
		LL x;
		cin>>x;
		st.insert(x);
	}
	LL x=st.size();
	LL d=0;
	LL ans=0;
	if (x==1){cout<<0<<endl; return;}
	bool f=st.count(0);
	st.erase(0);
	for (auto i:st){
		if (i>d){
			ans+=(i-d+x-1)/x;
			d+=(i-d+x-1)/x*x;
			f?:(f=1,x++);
		}
		x--;
	}
	cout<<ans;
}

元素的个数对答案无贡献,所以可以略去,采用 set 记录有哪些元素

可以发现只有当所有数都变为 \(0\) 才会使所有数相同(一开始都相同时除外)set 排序后每使一个元素变为 \(0\) ,那么种类数减小 \(1\)

考虑用 \(d\) 记录当前所有元素已经被减去了多少数值(set 内元素递增),对每个元素 \(i\) 而言,有两种情况:

  • \(i\le d\) :这个元素在之前已经被减少到了 \(0\) ,所以只会使得种类数减小 \(1\)
  • \(i>d\) :计算当前需要减去多少当前的种类数才能使得 \(i\) 变为 \(0\)减少时间复杂度的有效步骤

特别的,需要考虑 \(0\) 的存在:如果原数组中没有 \(0\) ,那么当第一个元素减少到 \(0\) 后,种类数不变

bool f=st.count(0);
st.erase(0);

for (auto i:st){
	if (i>d){
		ans+=(i-d+x-1)/x;//计算使i在减去之前的d后还需要多少步变为0,上取整
		d+=(i-d+x-1)/x*x;//累加d的数值
		f?:(f=1,x++);//如果原数组中无0,则种类数加1
	}
	x--;//每经过一个元素,种类数减少1
}

F:

题目大意:

const LL mod=1610612741;

int n;
LL cal[6010][6010];
LL fac[6010];

LL ksm(LL a,LL b,LL p){
	LL res=1;
	while (b){
		if (b&1) res=res*a%p;
		a=a*a%p;
		b>>=1;
	}
	return res;
}

void init(){
	fac[0]=1;
	for (int i=1;i<=n;i++)
		fac[i]=fac[i-1]*i%(mod-1);
	cal[0][0]=1;
	for (int base=1;base<=n;base++){
		cal[base][0]=1;
		for (int up=1;up<=n;up++)
			cal[base][up]=(cal[base-1][up]+cal[base-1][up-1])%(mod-1);
	}
}

void solve(){
	cin>>n;
	init();
	LL ans=1;
	for (LL mid=1;mid<=n;mid++){
		LL f=0;
		for (int len=1;len<=n;len++){
			LL t=0;
			if (len%2)
				t+=cal[mid-1][len/2]*cal[n-mid][len/2]%(mod-1);
			else
				t+=cal[mid-1][len/2]*cal[n-mid][len/2-1]%(mod-1);
			f+=t*fac[len]%(mod-1)*fac[n-len]%(mod-1)*(n-len+1)%(mod-1);
		}
		ans*=ksm(mid,f,mod);
		ans%=mod;
	}
	cout<<ans;
}

组合数学+贡献法

考虑每个中位数对答案的贡献,然后枚举区间 \([l,r]\) 的长度,对其他位上的数进行排列组合计数,累加贡献,可表示为

\[\prod _{mid=1}^n mid^{f(mid)} \]

其中 \(f(mid)\) 代表 \(mid\) 作为中位数的时候所有区间长度下的贡献总和,通过下列方程计算

\[f(mid)=\sum_{len=1}^n (n-len)!\times(n-len+1)\times\left[len!\times \binom{\lfloor\frac{len}{2}\rfloor}{mid-1} \times\binom{len-1-\lfloor\frac{len}{2}\rfloor}{n-mid} \right] \]

先考虑区间 \([l,r]\) 的放置位置,利用插空法先计算出其余元素的摆放方式,在某两个元素间插入区间 \([l,r]\)

\[\longrightarrow(n-len)!\times(n-len+1) \]

然后是区间内元素的摆放方式

\[\longrightarrow len! \]

要使得区间内的中位数是 \(mid\) ,那么小于 \(mid\) 的元素需要 \(\lfloor\frac{len}{2}\rfloor\) 个,大于 \(mid\) 的元素需要 \(len-1-\lfloor\frac{len}{2}\rfloor\) 个,分别在 \(mid-1\)\(n-mid\) 个元素中取

\[\longrightarrow\binom{\lfloor\frac{len}{2}\rfloor}{mid-1} \times\binom{len-1-\lfloor\frac{len}{2}\rfloor}{n-mid} \]

累加得到的 \(f(mid)\) 较大,采用快速幂仍然难以计算,考虑在模意义下的降幂处理

  • 费马小定理:若 \(p\) 为素数,对于任意的整数 \(a\) ,都有 \(a^p\equiv a({\rm{mod}}\ p)\)

公式变形得到 \(a^{p-1}\equiv 1({\rm{mod}}\ p)\) ,那么对于 \(a^b\)\(a^b\ {\rm{mod}}\ p=a^{b\ {\rm{mod}}\ (p-1)}\ {\rm{mod}}\ p\)

那么在模意义下的原方程为:

\[\prod _{mid=1}^n mid^{f(mid)\ {\rm{mod}}\ (p-1)}\ {\rm{mod}}\ p \]

因为题目给定的 \(1610612741\) 是一个质数,所以利用费马小定理进行降幂处理是十分正确的

posted @ 2025-06-08 22:11  才瓯  阅读(57)  评论(0)    收藏  举报