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;
}