AtCoder Beginner Contest 474

AtCoder Beginner Contest 474

A - Not X

要使得输入1,2,3后反回与输入不同且在1,2,3内的数.考虑在一个大小为3的环上移动,输出 \(x \bmod3+1\).

void solve(){
    int x;cin>>x;
    cout<<(x)%3+1<<endl;
}

B - Exit Order


判定当前离开的人是否属于该分组,先得出该位置隶属分组,然后计算出分组统率区间,最后判定当前人是否属于该分组。

void solve(){
    int n;cin>>n;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
        int rk=(i-1)/10+1;//得出分组
        int r=rk*10;//得出分组右区间
        int l=(rk-1)*10+1;//得出分组左区间
        if(a[i]>=l&&a[i]<=r) continue;
        cout<<"No"<<endl;
        return;
    }
    cout<<"Yes"<<endl;
}

C - Remove and Append


每次删除数值为x的数,然后插入末尾。求q次删除后数组状态。
维护排列中值为 \(x\) 的位置,考虑删除数后直接对该值的原位置打一个标记,然后在末尾加入该值。
每次如果遍历到删除标记就不进行输出。
这样维护时空复杂度均为 \(O(n+q)\)

void solve(){
    int n,q;
    cin>>n>>q;
    vector<ll>a(n+q+1),p(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
        p[a[i]]=i;
    }
    int ed=n+1;
    for(int i=1;i<=q;i++){
        int x;cin>>x;
        a[p[x]]=0;
        a[ed]=x;
        p[x]=ed;
        ++ed;
    }
    for(int i=1;i<=n+q;i++) {
        if(a[i]) cout<<a[i]<<' ';
    }
    cout<<endl;
}

D - Outweigh


如果存在某个位置$ a_i>b_i$ 直接赋值极大值\(w_i=10^{18}\),否则赋值极小值 \(w_i=1\),显然这样赋值最优,
且当至少一个 \(a_i>b_i\) 存在时一定有解。

void solve(){
    int n;cin>>n;
    const ll INF=1e18;
    vector<ll>a(n+1),b(n+1);
    vector<__int128>w(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];
    for(int i=1;i<=n;i++){
        if(a[i]>b[i]) w[i]=INF;
        else w[i]=1;
    }
    __int128 s=0;
    for(int i=1;i<=n;i++){
        s+=w[i]*a[i]-w[i]*b[i];
    }
    if(s>0){
        cout<<"Yes"<<endl;
        for(int i=1;i<=n;i++) cout<<(ll)w[i]<<' ';
        cout<<endl;
    }
    else cout<<"No"<<endl;
}

E - One Time Coupon


先默认都不使用优惠券这样总计可以获得 \(n\)张优惠券并消耗 \(s=\sum{a_i}\)的代价

然后因为可以重复购买某件商品,设$ mn=\min_{i=1}^n{a_i} $

然后按\(b_i-a_i\) 排序后进行贪心操作,

使得某件商品从 \(a_i\) 优惠到 \(b_i\)有两种方式:

  • 选择最便宜的商品购置获得优惠券.
  • 消耗已经获得的某张优惠券,并使得获得优惠券的位置不优惠(优惠后代价不优).

仅考虑 \(i\),\(j\)位置,初始代价为 \(a_i+a_j\),

使用方式1的产生代价为 \(2mn+b_i+b_j\)

使用方式2的产生代价为 \(b_i+a_j\)

因此只需要贪心比较 \(a_j-b_j\)\(2mn\)大小后操作一定最优

\(a_i-b_i \geq 2mn\) 时选用2操作,否则选用1操作.

void solve(){
    int n;cin>>n;
    multiset<ll>st;
    ll ans=0;
    vector<ll>a(n+1),b(n+1);
    ll mn=INF;
    for(int i=1;i<=n;i++) {
        cin>>a[i]>>b[i];
        ans+=a[i];
        mn=min(mn,a[i]);
        st.emplace(b[i]-a[i]);
    }
    int cnt=0;
    while(st.size()>=2&&ans+*st.begin()<ans){
        ll L=*st.begin(),R=*st.rbegin();
        if(R+mn*2<0) break;
        ans+=L;
        st.erase(st.find(L));
        st.erase(st.find(R));
    }
    while(!st.empty()){
        ll L=*st.begin();
        if(ans+L+mn<ans) ans+=L+mn;
        st.erase(st.find(L));
    }
    cout<<ans<<endl;
}

F - Increment All Divisors


设每个位置的最终状态值为\(C\),初始状态值为 \(A_i\),则每个位置需要的增量为 \(B_i =C-A_i\)

根据倍数关系可以设 每个位置操作次数为 \(x_i\),有\(B_i= \sum _{i \mid j} x_j\) 成立,现在假定 \(C\) 是已知常数,那么 \(B_i\)也是确定的,可以
进行莫比乌斯反演(倍数容斥)得出

\[B_i= \sum _{i \mid j,j=1}^n x_j \]

\[x_i= \sum _{i \mid j}^n \mu(j/i)B_j \]

\[x_i= \sum _{k=1}^{\lfloor \frac{n}{i} \rfloor}\mu(k)B_{ik} \]

\[x_i= \sum _{k=1}^{\lfloor \frac{n}{i} \rfloor}\mu(k)(C-A_{ik}) \]

\[x_i= C\sum _{k=1}^{\lfloor \frac{n}{i} \rfloor}\mu(k)- \sum _{k=1}^{\lfloor \frac{n}{i} \rfloor}\mu{(k)}A_{ik} \]

要取得C的下界,枚举 \(i\) ,设 \(Q=\sum _{k=1}^{\lfloor \frac{n}{i} \rfloor}\mu(k),P= \sum _{k=1}^{\lfloor \frac{n}{i} \rfloor}\mu{(k)}A_{ik}\),

要满足\(x_i \geq 0\),对于每一个\(i\),对应一个不等式按 \(Q\) 的符号进行分类

  • \(Q>0\),使得上界增加,\(R:=\max(R,\lceil \frac{P}{Q} \rceil)\)
  • \(Q=0\),若 \(P>0\) 无解.
  • \(Q<0\),\(L:=\min(L,\lfloor \frac{P}{Q} \rfloor)\)
    最后判定 \(L>R\)则无解,否则输出 \(L-a_1\)(因为每次操作都会选中1位置).
const int N=5e5+5;
int mu[N];
ll pmu[N],a[N];
bool isp[N];
vector<int>p;
const ll INF=1e18;
void solve(){
    memset(isp,true,sizeof isp);
    isp[0]=isp[1]=false;
    mu[1]=1;
    int n;cin>>n;
    ll L=0,R=INF;
    for(int i=1;i<=n;i++) cin>>a[i],L=max(a[i],L);
    for(int i=2;i<=n;i++){
        if(isp[i]){
            mu[i]=-1;
            p.push_back(i);
        }
        for(int x:p){
            if(x*i>n) break;
            mu[i*x]=-mu[i];
            isp[i*x]=false;
            if(i%x==0) {
                mu[i*x]=0;
                break;
            }
        }
    }
    for(int i=1;i<=n;i++) pmu[i]=pmu[i-1]+mu[i];
    for(int i=1;i<=n;i++){
        ll del=0;
        for(int k=1;k<=n/i;k++) del+=a[i*k]*mu[k];
        ll Q=pmu[n/i],P=del;
        if(Q>0) L=max(L,(ll)ceill(1.0*P/Q));
        else if(Q==0){
            if(P>0){
                cout<<-1<<endl;
                return;
            }
        }
        else R=min(R,(ll)floorl(1.0*P/Q));
    }
    if(L>R) cout<<-1<<endl;
    else cout<<L-a[1]<<endl;
}

方法二:由于每个 $B_i=C−A_i $都等于 \(x_i\) 加上所有 \(i\) 的真倍数的 \(x\) 之和,如果我们从大到小处理下标
\(i\),当我们处理到 \(i\) 时,所有满足 \(j>i\)\(i \mid j\)\(x_j\) 都已经“确定”,此时
\(B_i\) 中剩下的就只有 \(x_i\) 一项。于是可以立即利用 \(x_i \geq 0\) 得到关于 C 的一个线性约束。对 \(x_i\) 的线性式可以统一表示为:

\[x_i=b_i·C+c_i \]

初始时 \(b_i=1,c_i=−A_i\),即 \(x_i\) 尚未分离,\(b_iC+c_i\) 代表的是当前的 \(B_i\)。随着处理更大的倍数,我们会从
\(B_i\) 中减去那些已确定的 \(x_j\),从而更新 \(b_i,c_i\) 最终它们将恰好表示 \(x_i\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 1e18;
int main() {
    cin.tie(0)->ios::sync_with_stdio(false);
    int n;cin>>n;
    vector<vector<int>> divs(n + 1);
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j += i) {
            divs[j].push_back(i);
        }
    }
    vector<ll> a(n + 1), b(n + 1), c(n + 1);
    ll l = 1, r = INF;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        c[i] = -a[i];
        b[i] = 1;
    }
    for (int i = n; i >= 1; i--){
        // 处理 x_i = b_i * C + c_i >= 0 的约束
        if (b[i] > 0) {
            if (c[i] < 0) {
                l = max(l, (-c[i] + b[i] - 1) / b[i]);// C >= ceil(-c[i] / b[i])
            }
        } 
        else if (b[i] == 0){
            if (c[i] < 0) {
                cout << -1 << '\n';
                return 0;
            }
        } 
        else{ 
            r = min(r, c[i] / (-b[i]));// C <= floor(c[i] / (-b[i]))
        }// b[i] < 0
        for (int d : divs[i]) {
            if (d == i) continue;
            b[d] -= b[i];
            c[d] -= c[i];
        }// 从所有因子中消去 x_i
    }
    if (l > r) {
        cout << -1 << '\n';
        return 0;
    }
    // 按最小 C = l 模拟操作,计算总次数
    ll ans = 0;
    for (int i = n; i >= 1; i--) {
        ll add = l - a[i];
        ans += add;
        for (int d : divs[i]) a[d] += add;
    }
    cout << ans << '\n';
    return 0;
}
posted @ 2026-09-06 21:07  usedchang  阅读(64)  评论(0)    收藏  举报