牛客 小白月赛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]\) 的长度,对其他位上的数进行排列组合计数,累加贡献,可表示为
其中 \(f(mid)\) 代表 \(mid\) 作为中位数的时候所有区间长度下的贡献总和,通过下列方程计算
先考虑区间 \([l,r]\) 的放置位置,利用插空法先计算出其余元素的摆放方式,在某两个元素间插入区间 \([l,r]\)
然后是区间内元素的摆放方式
要使得区间内的中位数是 \(mid\) ,那么小于 \(mid\) 的元素需要 \(\lfloor\frac{len}{2}\rfloor\) 个,大于 \(mid\) 的元素需要 \(len-1-\lfloor\frac{len}{2}\rfloor\) 个,分别在 \(mid-1\) 和 \(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\)
那么在模意义下的原方程为:
因为题目给定的 \(1610612741\) 是一个质数,所以利用费马小定理进行降幂处理是十分正确的