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;
}
posted @ 2025-03-24 17:08  JoeyJiang  阅读(36)  评论(0)    收藏  举报