2024 Autumn Training #1 DF (by hzy)

D. 咸鱼跑酷(解有限trick)

大意:长度n跑道,每个点可以二选一道具(+ or * 一个正数),q个询问从初始分数u,从l跑到r,求最大分数(结果模P)。
可以预处理 \(mul_i\)\(add_i\) ,每个点要么乘要么加的数,把点分为两类,可乘点与不可乘点,\(mul_i=1\) 意味着 \(i\) 点不可乘只能加,决策固定,因此我们需要考虑可乘点的决策。可以发现每次可乘点至少*2,经过 \(\log W\) 次可乘点后,后续的可乘点的决策必为*。
故可以模拟前\(\log\)次可乘点决策,不可乘点用前缀和处理。之后,结果的贡献有2部分,一是 \(u\) 乘后面所有可乘点,用 \(mul_i\) 的后缀积 \(suf\_mul_i\) 处理,二是后面所有不可乘点的\(\sum add_i*suf\_mul_i\),用后缀和处理。注意模意义下0的处理,需统计0的个数。

#include <bits/stdc++.h>
#define ll long long
#define N 100005
#define P 998244353LL
#define INF 1000000000
using namespace std;
int mul[N], add[N];
int nxt[N]; // 下一个可乘点
ll sa[N]; // 不可乘点前缀和
ll suf_mul[N]; // mul后缀积(不含0)
int suf_0[N]; // mul后缀0计数
ll suf_am[N]; // 不可乘点带权后缀和
int n,q;
ll inv(ll x)
{
    ll res=1, b=P-2;
    while(b)
    {
        if(b&1) res = res*x%P;
        b>>=1;
        x = x*x%P;
    }
    return res;
}
void solve()
{
    cin>>n;
    char op[15];
    for(int i=1;i<=n;++i)
    {
        mul[i]=1; add[i]=0;
        auto f = [&]()->void 
        {
            cin>>op;
            if(op[0]=='+') add[i] = max(add[i], atoi(op+1));
            else mul[i] = max(mul[i], atoi(op+1));
        };
        f(); f();

        sa[i] = sa[i-1];
        if(mul[i]==1) sa[i] += add[i];
    }
    nxt[n]=n+1;
    suf_mul[n+1]=1;
    for(int i=n;i>=1;--i)
    {
        nxt[i-1]=nxt[i];
        if(mul[i]>1) nxt[i-1]=i;

        suf_mul[i] = suf_mul[i+1];
        suf_0[i] = suf_0[i+1];
        suf_am[i] = suf_am[i+1];

        if(mul[i]%P == 0) ++suf_0[i];
        else suf_mul[i] = (suf_mul[i]*mul[i]) % P;
        if(mul[i]==1) suf_am[i]=(suf_am[i] + suf_mul[i]*add[i]) % P;
    }
    cin>>q;
    while(q--)
    {
        ll u;
        int l,r;
        cin>>u>>l>>r;
        int pos = l-1;
        while(nxt[pos]<=r && u<INF)
        {
            u += sa[nxt[pos]]-sa[pos]; // 1e14?
            pos = nxt[pos];
            if(u >= INF) {--pos; break;}
            u = max(u*mul[pos], u+add[pos]);
        }
        u %= P;
        ll a,b;

        a = (suf_0[pos+1] == suf_0[r+1]) ? suf_mul[pos+1]*inv(suf_mul[r+1])%P : 0;
        int k = lower_bound(suf_0+pos+1, suf_0+r+1, suf_0[r+1], greater<int>())-suf_0;
        b = (suf_am[k]+P-suf_am[r+1])%P*inv(suf_mul[r+1])%P;
        cout<<(u*a + b) % P<<'\n';
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T=1;
    // cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}

F. 羁绊大师(图论、bitset优化背包)

大意:一张简单图,m个点n条边,度至多为2,点被激活当且仅当它连的2条边激活。求i从1到n,选取i条边,能激活的最多的点数。
此图可以分成若干个链、环,先统计每个链、环的大小,选环更优,没有环再选链,对环做01背包(1e5级别,需要bitset位运算优化)然后贪心即可。

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define ll long long
#define N 200005
#define M 100005
#define MOD 998244353
using namespace std;
int n,m;
bitset<N> vis;
bitset<M> kd;
vector<int> e[N];
vector<int> c, l;
void dfs(int u,int cnt)
{
    bool ok=false;
    for(auto &&v:e[u])
    {
        if(vis[v]) continue;
        vis[v]=1;
        ok=true;
        dfs(v,cnt+1);
    }
    if(!ok)
    {
        if(e[u].size()==1) l.push_back(cnt-1);
        if(e[u].size()==2) c.push_back(cnt);
    }
}
void solve()
{
    cin>>m>>n;
    for(int i=1;i<=m;++i)
    {
        int u,v; cin>>u>>v;
        e[u].push_back(v); e[v].push_back(u);
    }
    for(int i=1;i<=n;++i)
    {
        if(!vis[i] && e[i].size()==1) {vis[i]=1; dfs(i,1);}
    }
    for(int i=1;i<=n;++i)
    {
        if(!vis[i]) {vis[i]=1; dfs(i,1);}
    }
    kd[0]=1;
    int sum=0;
    for(auto &&w:c)
    {
        sum+=w;
        kd|=kd<<w;
    }
    
    sort(l.begin(),l.end(),greater<int>());
    // cout<<l.size()<<'\n';
    // for(auto &&w:l) cout<<w<<' '; cout<<'\n';
    int d=0,j=0,tmp=0;
    for(int i=1;i<=m;++i)
    {
        if(kd[i]) {d=0,j=0,tmp=0;}
        else {++d; if(tmp<d && i>sum){tmp+=l[j++];}}
        
        if(i<=sum) cout<<i-(d>=1)<<' ';
        else cout<<i-j<<' ';
    }
    cout<<'\n';
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T=1;
    // cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}
posted @ 2024-09-29 20:04  DP_PTSD  阅读(3)  评论(0编辑  收藏  举报