快来踩爆这个蒟蒻吧|

Little_corn

园龄:1年1个月粉丝:11关注:17

2025-02-26 15:06阅读: 9评论: 0推荐: 0

rec.md

大概就是做一些省选的题吧,然后还剩下的东西大概就是会等到省选完再学吧。

省选2024 迷宫守卫

之前没有做过的,现在来补一下。

注意到第一个点是很重要的,先考虑第一个点选什么然后一个一个推下去,显然有朴素的 DPfu,k 表示以 u 节点的子树中,给了 k 块钱,可以让答案 fu,k 中的 fu,k 的最大值。

转移显然 fu,j=fls,j+min(frs,j,wu),初始化 fu,0qu=0,fu,qu+1=+,然后由于一层中第二维的总和是 2n,维护拐点即可做到 O(2nn2)/O(2nn)(使用归并排序)。

关键是如何求答案。考虑定义 sol(u,k) 为考虑 u 子树内最优的排列,且给了 k 块钱,花费的钱数。

考虑 fu,jk 的最大 j,那么下一个走到的数一定是 j。找到 j 所在的子树。

  • j 在右子树:那么 wu 一定不会选,由于是先访问右子树,那么需要尽量给右子树更多的钱,于是我们预留左子树需要的钱数 fls,j,执行 cst=sol(rs,kfls,j),sol(ls,kcst)

  • j 在左子树:考虑 wu 是否会被选择,首先执行 cst=sol(ls,kmin(frs,j,wu))。由于选择 wu 会导致 Alice 可以直接使用的钱变少,肯定是更劣的,于是我们能不选就不选,即 fu,rskcst 时,我们才会选择 wu。然后执行 sol(rs,kcstwu/0) 即可。

这样时间复杂度就是 O(2nn),码量不大,大概 1h+δ 写+调。

qwq
#include<bits/stdc++.h>
#define ll long long
//#define int long long
#define pir pair<int, ll>
#define pb emplace_back
#define ls (u << 1)
#define rs (u << 1 | 1)
#define lf(x) ((x >= (1ll << n)))
#define fi first
#define se second
using namespace std;
const int N = (1ll << 17) + 10;
const ll INF = 1e18;
int n;
ll w[N], q[N], rk[N];
vector<pir> vec[N];
inline void dp(int u){
if(lf(u)){vec[u].pb(make_pair(q[u], 0ll)); return;}
dp(ls); dp(rs); int siz = vec[ls].size() - 1; //vec[ls].pb(pINF); vec[rs].pb(pINF);
int i = siz, j = siz;
while(i >= 0 && j >= 0){
int val = 0;
if(vec[ls][i].fi > vec[rs][j].fi) val = vec[ls][i].fi, i--;
else val = vec[rs][j].fi, j--;
vec[u].pb(make_pair(val, ((i != siz) ? vec[ls][i + 1].se : INF) + min(w[u], ((j != siz) ? vec[rs][j + 1].se : INF))));
}
while(i >= 0) vec[u].pb(make_pair(vec[ls][i].fi, vec[ls][i].se + min(vec[rs][0].se, w[u]))), i--;
while(j >= 0) vec[u].pb(make_pair(vec[rs][j].fi, vec[ls][0].se + min(vec[rs][j].se, w[u]))), j--;
reverse(vec[u].begin(), vec[u].end());
// cerr << u << "\n";
// for(auto qwq : vec[u]) cerr << qwq.fi << " " << qwq.se << "\n";
}
inline int findpos(int u, ll val){
int ret = 0, L = 0, R = vec[u].size() - 1;
while(L <= R){
int mid = (L + R >> 1);
if(vec[u][mid].se <= val) L = mid + 1, ret = vec[u][mid].fi;
else R = mid - 1;
}
return ret;
}
inline ll getf(int u, int j){
ll ret = 0, L = 0, R = vec[u].size() - 1;
while(L <= R){
int mid = (L + R >> 1);
if(vec[u][mid].fi >= j) ret = vec[u][mid].se, R = mid - 1;
else L = mid + 1;
}
if(L == vec[u].size() && (!ret)) ret = INF;
// cerr << u << " " << j << " " << ret << "\n";
return ret;
}
inline ll sol(int u, ll k, int dep){
// cerr << u << " " << k << "\n";
if(lf(u)){cout << q[u] << " "; return 0;}
int j = findpos(u, k); ll ret = 0;
// cerr << u << " " << j << "\n";
if(rk[j] & (1ll << (n - dep - 1))){
ret = sol(rs, k - getf(ls, j), dep + 1);
ret += sol(ls, k - ret, dep + 1);
} else {
ll fr = getf(rs, j);
ret += sol(ls, k - min(fr, w[u]), dep + 1);
if(fr > k - ret) ret += w[u];
ret += sol(rs, k - ret, dep + 1);
} return ret;
}
inline void solve(){
ll k; cin >> n >> k;
for(int i = 1; i < (1ll << n); i++) cin >> w[i];
for(int i = (1ll << n); i < (1ll << (n + 1)); i++) cin >> q[i], rk[q[i]] = i;
dp(1); sol(1, k, 0); cout << "\n";
}
inline void clr(){
for(int i = 1; i < (1ll << (n + 1)); i++) vec[i].clear();
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T; cin >> T; while(T--) clr(), solve();
return 0;
}

ABC245Ex Product Modulo 2

Trick:根据 CRT 的结论,假设确定了每个质数维度上的余数,那么就可以唯一确定原模数的取值。于是我们分开每一类质数考虑,即考虑 m=pk 的情况。

经典的,我们先不考虑最后一位的取值,而是考虑前 n1 位最后的乘积在 p 这一维上的表现。具体的,设 fi,j 是前 i 位,乘积中恰好只含 pj,即可以表示为 x×pjgcd(x,p)=1),于是有转移 fi,j×ccbkfi+1,min(mx,j+k)。其中 ccbi0m1 中恰好含 pi 项的数的个数,容易转 解决。不难发现每一轮的转移都是一致的,于是可以矩阵快速幂做到 O(log3mlogn)

然后考虑假设前 i 位的乘积可以表示成 x1×py1,最后的结果可以表示成 x2×py2。那么相当于要求最后一个数 l=x×py 满足 y=y2y1。可以改写为:

x1×xx2(modpmxy1y)

由于 gcd(p,x1)=1,于是 xx2×x11(modpmx)。提取出来即 x=x2×x11+wpmxy1y。即求有多少个 w 满足条件,不难发现由于 x1[0,pmxb),于是 w[0,pa)。即最后的答案为 i=0mx[Kmodpi=0]fn1,i×pi

注意 Kmodpmx=0 的情况,直接乘上 fn,mx 即可。

qwq
#include<bits/stdc++.h>
#define ll long long
//#define int long long
#define pir pair<int, int>
#define pb emplace_back
using namespace std;
const int N = 40;
const ll mod = 998244353;
ll n, m, A, ccb[N], pw[N];
inline void ADD(ll& x, ll y){x += y; (x >= mod) ? (x -= mod) : 0ll;}
struct mat{
ll a[N + 5][N + 5];
mat(){memset(a, 0, sizeof a);}
inline void I(){
for(int i = 0; i < N; i++) a[i][i] = 1;
}
};
inline mat operator*(struct mat A, struct mat B){
mat ret;
for(int k = 0; k < N; k++)
for(int i = 0; i < N; i++)
for(int j = 0; j < N; j++) ADD(ret.a[i][j], A.a[i][k] * B.a[k][j] % mod);
return ret;
}
inline mat mpow(mat bas, ll y){
mat ans; ans.I();
for(; y; y >>= 1, bas = bas * bas) if(y & 1) ans = ans * bas;
return ans;
}
inline ll solvesi(ll p, int mx){
//cout << p << " " << mx << "\n";
ll ans = 0; mat bas; pw[0] = 1;
for(int i = 1; i <= mx; i++) pw[i] = pw[i - 1] * p;
for(int i = 0; i < mx; i++) ccb[i] = (pw[mx - i] - pw[mx - i - 1]) % mod;
ccb[mx] = 1;
for(int i = 0; i <= mx; i++){
for(int j = 0; j <= mx; j++) ADD(bas.a[i][min(mx, i + j)], ccb[j]);
}
mat ret; ret.a[0][0] = 1;
if(A % pw[mx] == 0){
ret = ret * mpow(bas, n);
// cout << ret.a[0][mx] << "\n";
return ret.a[0][mx];
}
ret = ret * mpow(bas, n - 1);
for(int i = 0; i <= mx; i++){
if(A % pw[i] != 0) break;
ADD(ans, ret.a[0][i] * pw[i] % mod);
}
return ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> A >> m; ll ans = 1;
for(ll i = 2; i * i <= m; i++){
if(m % i == 0){
int cnt = 0;
while(m % i == 0) m /= i, cnt++;
ans = ans * solvesi(i, cnt) % mod;
}
} if(m > 1) ans = ans * solvesi(m, 1) % mod;
cout << ans;
return 0;
}

本文作者:Little_corn

本文链接:https://www.cnblogs.com/little-corn/p/18738529

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Little_corn  阅读(9)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起