USACO 2025 Open, Gold
A - Moo Decomposition
题目大意
给定由 \(\tt M\) 和 \(\tt O\) 组成的长度为 \(n\) 的串 \(s\),求将 \(s^l\) 划分为 \(\mathtt{MO}^k\) 的方案数 \(\bmod 10^9+7\)。
数据范围
-
\(1\leq k\leq n\leq 10^6\)。
-
\(1\leq l\leq 10^{18}\)。
题解
记 \(t = s^l\)。
从后向前考虑,记 \(\displaystyle O_i = \sum_{i=1}^{nl}[t_i = \mathtt{O}]\),\(\displaystyle M_i = \sum_{i=1}^{nl} [t_i = \mathtt{M}]\)。
则答案为:\(\displaystyle (\sum_{i=1}^{nl} \binom{O_i-k(M_i-1)}{k} [t_i = \mathtt{M}])[M_1=kO_1]\)。
注意到若 \(M_1=kO_1\) 则 \(M_{dn+1}=kO_{dn+1}\)。
换句话说若将 \(s\) 划分为 \(l\) 段的话,每一段是独立的,不会给前面的段留有剩余。
所以原式等于 \(\displaystyle (\sum_{i=1}^{n} \binom{O_i-k(M_i-1)}{k} [s_i = \mathtt{M}])^l[M_1=kO_1]\)。
这个式子可以 \(O(n+\log l)\) 计算。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e6+9;
const int mod=1e9+7;
inline int QPow(int x,ll y){
int res=1;
while(y){
if(y&1) res=1ll*res*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return res;
}
#define Inv(x) QPow(x,mod-2)
int fac[N],ifac[N];
inline void Init(int lim){
fac[0]=1;
for(int i=1;i<=lim;i++) fac[i]=1ll*fac[i-1]*i%mod;
ifac[lim]=Inv(fac[lim]);
for(int i=lim-1;~i;i--) ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}
inline int C(int n,int m){
if(m>n||m<0) return 0;
else return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
ll t;
int n,k;
char c[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
#define endl '\n'
cin>>k>>n>>t;
for(int i=1;i<=n;i++) cin>>c[i];
Init(n);
int cnt=0,ans=1;
for(int i=n;i>=1;i--){
if(c[i]=='O') cnt++;
else{
ans=1ll*ans*C(cnt,k)%mod;
cnt-=k;
}
}
if(cnt) ans=0;
cout<<QPow(ans,t)<<endl;
return 0;
}
B - Election Queries
题目大意
记 \(\mathrm{mode}(S)\) 为可重集 \(S\) 中众数集合。
定义 \(\displaystyle F(S)=\max_{T\subset S \wedge T \neq \emptyset} \max_{i\in \mathrm{mode}(T),j\in \mathrm{mode}(S/T)} |i-j|\)。
给定大小为 \(n\) 的可重集 \(A\),有 \(q\) 次操作,每次替换 \(A\) 中的一个元素,求每次替换后的 \(F(A)\)。
数据范围
-
\(1\leq n\leq 2\times 10^5\)。
-
\(\forall x\in S\),有 \(1\leq x\leq n\)。
-
\(1\leq q\leq 10^5\)。
题解
记 \(tot_x\) 为 \(A\) 中 \(x\) 的出现次数,不难发现题目中的限制等价于 \(\displaystyle tot_i+tot_j\geq \max_k tot_k\)。
考虑对 \(lim=\displaystyle \max_k tot_k\) 阈值分治:
-
对于 \(lim\leq B\) 的情况:
记 \(\displaystyle mx_i=\max_{tot_j\geq i} j\),\(\displaystyle mn_i=\min_{tot_j\geq i} j\)。考虑枚举最后可能的 \(tot_i\),则答案为 \(\displaystyle \max_{i\geq 1} mx_i-mn_{lim-i}\)。
-
对于 \(lim \gt B\) 的情况:
称 \(tot_x \geq \lceil \frac B2 \rceil\) 的 \(x\) 为大数,则由 \(\displaystyle \sum_i tot_i =n\) 可知大数的个数不会超过 \(\lfloor\frac{2n}B\rfloor\) 个,同时一组合法的 \((i,j)\) 中至少有一个大数。考虑枚举大数,则答案为:\(\displaystyle \max_{tot_i \geq \lceil \frac B2 \rceil} \max\{i-mn_{lim-tot_i},mx_{lim-tot_i}-i\}\)。
现在的问题是如何 \(O(1)\) 查 \(mn_i\) 和 \(mx_i\)。
使用 set 做到 \(O(\log n)\) 查询是简单的。发现每次修改 \(\Delta tot_i \in \{-1,0,1\}\),所以实际上受到影响的只有 \(O(1)\) 个位置,丢到数组上先修改后查询即可。
时间复杂度 \(O(n\log n+q(\log n+\sqrt n))\)。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+9;
const int B=4e2;
int a[N],cnt[N],mx[N],mn[N],n,q;
set<int> s[N],all,big;
inline void Update(int x){
mx[x]=mx[x+1],mn[x]=mn[x+1];
if(s[x].size()){
mx[x]=max(mx[x],*s[x].rbegin());
mn[x]=min(mn[x],*s[x].begin());
}
}
inline void Veto(int x){
all.erase(cnt[x]);
s[cnt[x]].erase(x);
Update(cnt[x]);
if(cnt[x]>=(B>>1)) big.erase(x);
cnt[x]--;
if(cnt[x]>=(B>>1)) big.insert(x);
all.insert(cnt[x]);
s[cnt[x]].insert(x);
}
inline void Vote(int x){
all.erase(cnt[x]);
s[cnt[x]].erase(x);
if(cnt[x]>=(B>>1)) big.erase(x);
cnt[x]++;
if(cnt[x]>=(B>>1)) big.insert(x);
all.insert(cnt[x]);
s[cnt[x]].insert(x);
Update(cnt[x]);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
#define endl '\n'
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
mx[n+1]=0,mn[n+1]=n+1;
for(int i=1;i<=n;i++) cnt[a[i]]++;
for(int i=1;i<=n;i++) s[cnt[i]].insert(i);
for(int i=n;i>=0;i--) Update(i);
for(int i=1;i<=n;i++) all.insert(cnt[i]);
for(int i=1;i<=n;i++) if(cnt[i]>=(B>>1)) big.insert(i);
while(q--){
int i,x;
cin>>i>>x;
Veto(a[i]);
a[i]=x;
Vote(a[i]);
int lim=*all.rbegin();
if(lim<=B){
int ans=0;
for(int i=1;i<=lim;i++){
ans=max(ans,mx[max(lim-i,1)]-mn[i]);
ans=max(ans,mx[i]-mn[max(lim-i,1)]);
}
cout<<ans<<endl;
}else{
int ans=0;
for(int i:big){
ans=max(ans,mx[max(lim-cnt[i],1)]-i);
ans=max(ans,i-mn[max(lim-cnt[i],1)]);
}
cout<<ans<<endl;
}
}
return 0;
}
C - OohMoo Milk
题目大意
给定数组长度为 \(n\) 的数组 \(a\),重复执行以下操作 \(d\) 次:
-
将 \(a\) 从大到小排序。
-
\(\forall s\lt i\leq t\),将 \(a_i\) 加一。
求 \(\displaystyle\sum_{i=1}^n a_i^2\)。
数据范围
-
\(1\leq s,t \leq n\leq 10^5\)。
-
\(\forall 1\leq i\leq n\),有 \(1\leq a_i \leq 10^9\)。
-
\(1\leq d\leq 10^9\)。
题解
去除 \(a_{t+1}\) 到 \(a_n\) 后,对每个位置二分答案即可,时间复杂度 \(O(n\log V)\)。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e5+9;
const int mod=1e9+7;
int a[N],lft[N],n,m,len,k;
ll pre[N];
inline ll F(int l,int r,int k){return 1ll*(r-l+1)*k-(pre[r]-pre[l-1]);}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
#define endl '\n'
cin>>n>>m>>len>>k;
k=len-k;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1,greater<int>());
sort(a+1,a+len+1,less<int>());
ll rem=1ll*m*k,ans=0;
for(int i=1;i<=len;i++) pre[i]=pre[i-1]+a[i];
for(int i=1,j=1,lst=a[1];i<=len;i++){
int l=max(a[i],lst),r=a[i]+m+1;
while(j<=len&&F(i,j,a[j])<=rem) j++;
while(l+1<r){
int mid=ll(l)+ll(r)>>1;
if(F(i,j-1,mid)<=rem) l=mid;
else r=mid;
}
rem-=l-a[i];
lst=l;
ans+=1ll*l*l%mod;
}
for(int i=len+1;i<=n;i++) ans+=1ll*a[i]*a[i]%mod;
cout<<ans%mod<<endl;
return 0;
}

浙公网安备 33010602011771号