Welcome to the Nigh|

XiaoLe_MC

园龄:1年2个月粉丝:3关注:9

[考试记录] 2024.11.12 noip模拟赛11

T1 送信卒

使用 bfs 记录走到 tx,ty 的路径的横边和竖边的数量,然后取 max。这里取 max 的原因是,找到的路径必须是最短路,当 k 取的小的时候竖边就会变多,所以这条路径就不一定是最短路了。

#include<bits/stdc++.h>
using namespace std;
#define p pair<int, int>
int n, m, sx, sy, tx, ty;
int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
bitset<105> mp[105], vis[105];
double s;
struct node{ int x, y, w, h; };
queue<node> q;
vector<p> vec;
int main(){
freopen("msg.in", "r", stdin); freopen("msg.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>m>>sx>>sy>>tx>>ty;
for(int i=1, x; i<=n; ++i) for(int j=1; j<=m; ++j)
cin>>x, mp[i][j] = x;
cin>>s;
q.emplace(node{sx, sy, 0, 0});
while(!q.empty()){
auto t = q.front(); q.pop();
int ux = t.x, uy = t.y;
vis[ux][uy] = 1;
for(int i=0; i<4; ++i){
int vx = ux + dir[i][0], vy = uy + dir[i][1];
if(vx < 1 || vy < 1 || vx > n || vy > m || mp[vx][vy] || vis[vx][vy]) continue;
int tmpw = t.w, tmph = t.h;
dir[i][0] ? ++tmph : ++tmpw;
if(vx == tx && vy == ty){
vec.emplace_back(p{tmpw, tmph});
continue;
} vis[vx][vy] = 1;
q.emplace(node{vx, vy, tmpw, tmph});
}
} double ans = 0.0;
for(auto it : vec){
int w = it.first, h = it.second;
if(w * 1.0 > s) continue;
ans = max(ans, 1.0 * (s - 1.0 * w) / h);
} return cout<<fixed<<setprecision(3)<<ans, 0;
}

T2 共轭树图

看起来很屌的一道题,考场上被吓住了。令 fu,i 表示节点 u 子树内节点向前 i 个祖先连边的贡献。那么有

fu,i=j=1ivsonufv,i+1

使用前缀和即可做到 O(N2)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
constexpr int N = 3e3 + 5, M = 998244353;
int n, dep[N], f[N][N];
vector<int> G[N];
inline int add(initializer_list<int> Add){
int res = 0;
for(int v : Add) res = res + v >= M ? res + v - M : res + v;
return res;
}
inline int mul(initializer_list<int> Mul){
int res = 1;
for(int v : Mul) res = (ll)res * v % M;
return res;
}
inline void dfs1(int u, int fa){
for(int v : G[u]) if(v ^ fa)
dep[v] = dep[u] + 1, dfs1(v, u);
}
inline void dfs2(int u, int fa){
if((u ^ n) && G[u].size() == 1){
for(int i=1; i<=dep[u]; ++i) f[u][i] = i;
return;
}
vector<int> tmp(dep[u]+3, 1);
for(int v : G[u]) if(v ^ fa){
dfs2(v, u);
for(int i=2; i<=dep[v]; ++i)
tmp[i] = mul({tmp[i], f[v][i]});
}
for(int i=1; i<=dep[u]; ++i)
f[u][i] = add({f[u][i-1], tmp[i+1]});
}
int main(){
freopen("reflection.in", "r", stdin); freopen("reflection.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n; for(int i=1, u, v; i<n; ++i){
cin>>u>>v;
G[u].emplace_back(v), G[v].emplace_back(u);
} dep[n] = 1, dfs1(n, 0), dfs2(n, 0); int ans = 1;
for(int v : G[n]) ans = mul({ans, f[v][1]});
return cout<<ans, 0;
}

T3 摸鱼军训

考虑每一次冒泡都会把一个大数提到序列最后面,那么相应地就会有较小的数向前移动。那么就可以大致描述某一个数的移动过程:先向前走一段,再向后走一段。显然有每个数向前走的距离为这个数前面比它大的数的数量,称为 lmi,如果查询的 k 要小于等于 lmi,那么答案即为 posik

对于向后走的一段,考虑使用一个队列来更直观的维护这个东西。每一次冒泡之前都将向前走走完的数加到队列中去,这个队列一定是单调递增的。对于如下队列:(中间加点是为了凸显在原数列中的位置)

4567

那么下一次冒泡,序列变化如下:

4567 45674567

可以发现的是每一次冒泡,队列里的每一个数,都会移动到它的下一个数的屁股后面。又因为队列里的数的顺序是按照向前移动完的时间加进去的,那么比 x 大的数一定会先加入队列中。那么对于第一次移动,x会移动到 第一个大于它的数 y 的屁股后面,也就是 posy1,第二次移动时,y 会移动到 z 的屁股后面,所以 posy=posz1,然后 x 移动到 y 的后面 posx=posy1=posz2。规律显然。对于第 k 次移动,在队列里第 k 个的数的位置为 posk,那么答案即为 poskk。又因为在队列里比他靠前的数只能是比它大的数,那么就在原序列里找从左往右数第 k 个大于它的数的 pos 即可。

#include<bits/stdc++.h>
using namespace std;
constexpr int B = 1<<20;
char buf[B], *p1 = buf, *p2 = buf, obuf[B], *O = obuf;
#define gt() (p1==p2 && (p2=(p1=buf)+fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++)
template <typename T> inline void rd(T &x){
x = 0; int f = 0; char ch = gt();
for(; !isdigit(ch); ch = gt()) f ^= ch == '-';
for(; isdigit(ch); ch = gt()) x = (x<<1) + (x<<3) + (ch^48);
x = f ? -x : x;
}
template <typename T, typename ...TT> inline void rd(T &x, TT &...xx){ rd(x), rd(xx...); }
#define pt(ch) (O-obuf==B && (fwrite(obuf, 1, B, stdout), O=obuf), *O++ = (ch))
template <typename T> inline void wt(T x){
if(x > 9) wt(x / 10); pt(x % 10 ^ 48);
}
#define fw fwrite(obuf, 1, O - obuf, stdout)
#define p pair<int, int>
constexpr int N = 5e5 + 5;
int n, q, a[N], ans[N], pos[N], lm[N];
vector<p> Q[N];
namespace BIT{
#define lb(x) ((x) & (-x))
int t[N];
inline void add(int pos, int val){
for(; pos<=n; pos+=lb(pos)) t[pos] += val;
}
inline int query(int pos){
int res = 0;
for(; pos>0; pos-=lb(pos)) res += t[pos];
return res;
}
}
namespace ST{
#define ls (id << 1)
#define rs (id << 1 | 1)
struct node{ int l, r, sum; }t[N<<2];
inline void build(int id, int l, int r){
t[id] = node{l, r, 0};
if(l == r) return;
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid+1, r);
}
inline void modify(int id, int pos){
if(t[id].l == t[id].r) return t[id].sum = 1, void();
int mid = (t[id].l + t[id].r) >> 1;
modify((pos <= mid) ? ls : rs, pos);
t[id].sum = t[ls].sum + t[rs].sum;
}
inline int query(int id, int val){
if(t[id].l == t[id].r) return t[id].l;
int mid = (t[id].l + t[id].r) >> 1;
if(t[ls].sum >= val) return query(ls, val);
return query(rs, val-t[ls].sum);
}
}
int main(){
freopen("bubble.in", "r", stdin); freopen("bubble.out", "w", stdout);
rd(n); for(int i=1; i<=n; ++i){
rd(a[i]), BIT::add(a[i], 1);
lm[a[i]] = i - BIT::query(a[i]);
pos[a[i]] = i;
}
rd(q); for(int i=1, k, x; i<=q; ++i){
rd(k, x);
if(lm[x] >= k) ans[i] = pos[x] - k;
else if(n - x < k) ans[i] = x;
else Q[x].emplace_back(p{k, i});
}
ST::build(1, 1, n);
for(int i=n; i>=1; --i){
for(auto it : Q[i]){
int ps = ST::query(1, it.first);
ans[it.second] = ps - it.first;
} ST::modify(1, pos[i]);
}
for(int i=1; i<=q; ++i) wt(ans[i]), pt(10);
return fw, 0;
}

T4 神奇园艺师

首先,如果将这个子集中每个数的质因子 p 指数提取出来并排个序,那么最终的序列里 p 的指数必然其为中位数,操作次数即为 |xxm|

考虑将贡献拆解。将整个序列的指数 p 提取出来排个序,枚举第 i 位,考虑第 i 个数的指数 p 能给答案做出怎样的贡献。不妨考虑在 i 左边选择 a 个数,右边选择 b 个数。因为是有序的,所以当 a>b 的时候中位数一定在 a 里边取得,设中位数为 xm,那么应有 xixm,但可以发现的是,中位数的贡献是一定可以拆绝对值之后左右消掉的,所以贡献应为 xi×(i1a)×(nib)。复杂度 O(N3)

最后的优化需要范德蒙德卷积,没写完……

#include<bits/stdc++.h>
using namespace std;
constexpr int B = 1<<20;
char buf[B], *p1 = buf, *p2 = buf, obuf[B], *O = obuf;
#define gt() (p1==p2 && (p2=(p1=buf)+fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++)
template <typename T> inline void rd(T &x){
x = 0; int f = 0; char ch = gt();
for(; !isdigit(ch); ch = gt()) f ^= ch == '-';
for(; isdigit(ch); ch = gt()) x = (x<<1) + (x<<3) + (ch^48);
x = f ? -x : x;
}
template <typename T, typename ...TT> inline void rd(T &x, TT &...xx){ rd(x), rd(xx...); }
#define pt(ch) (O-obuf==B && (fwrite(obuf, 1, B, stdout), O=obuf), *O++ = (ch))
template <typename T> inline void wt(T x){
if(x > 9) wt(x / 10); pt(x % 10 ^ 48);
}
#define fw fwrite(obuf, 1, O - obuf, stdout)
constexpr int N = 1e6 + 5, M = 1e9 + 7, PM = 8e4;
int n, a[N], inv[N], fac[N], mx, dv[N], s[N];
vector<int> pr, P[N]; bitset<N> vis;
inline int qpow(int a, int k){
int res = 1; while(k){
if(k & 1) res = (long long)res * a % M;
a = (long long)a * a % M; k >>= 1;
} return res;
}
inline int add(initializer_list<int> Add){
int res = 0;
for(int v : Add) res = res + v >= M ? res + v - M : res + v;
return res;
}
inline int mul(initializer_list<int> Mul){
int res = 1;
for(int v : Mul) res = (long long)res * v % M;
return res;
}
inline int mod(int x){ return (x + M) % M; }
inline int C(int a, int b){ return b > a ? 0 : mul({fac[a], inv[b], inv[a-b]}); }
int main(){
freopen("game.in", "r", stdin); freopen("game.out", "w", stdout);
rd(n); for(int i=1; i<=n; ++i) rd(a[i]), mx = max(mx, a[i]);
fac[0] = inv[0] = 1; for(int i=1; i<=n; ++i) fac[i] = mul({fac[i-1], i});
inv[n] = qpow(fac[n], M-2); for(int i=n-1; i>=1; --i) inv[i] = mul({inv[i+1], i+1});
s[0] = C(n-1, 0); for(int i=1; i<n; ++i) s[i] = add({s[i-1], C(n-1, i)});
for(int i=2; i<=mx; ++i){
if(!vis[i]) pr.emplace_back(i), dv[i] = i;
for(int v : pr){
if((long long)v * i > mx) break;
vis[v*i] = 1; dv[v*i] = v;
if(!(i % v)) break;
}
}
for(int i=1; i<=n; ++i){
int v = a[i]; while(v > 1){
int p = dv[v], cnt = 0;
while(!(v % p)) ++cnt, v /= p;
P[p].emplace_back(cnt);
}
}
int ans = 0;
for(int v : pr) if(!P[v].empty()){
sort(P[v].begin(), P[v].end());
for(int j=n-P[v].size()+1, ps=0; j<=n; ++j, ++ps){
int res = add({(n-j-1 >= 0 ? mod(-s[n-j-1]) : 0), s[j-2]});
ans = add({ans, mul({res, P[v][ps]})});
}
} return cout<<ans, 0;
}

本文作者:XiaoLe_MC

本文链接:https://www.cnblogs.com/xiaolemc/p/18542463

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

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