W
e
l
c
o
m
e
: )

[考试记录] 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 共轭树图

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

\[f_{u,i}=\sum_\limits{j=1}^{i}\prod_\limits{v\in son_u}f_{v,i+1} \]

使用前缀和即可做到 \(\mathcal{O}(N^2)\)

#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 摸鱼军训

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

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

\[4 \dots5\dots6\dots7 \]

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

\[\begin{split} 4 \dots5\dots6\dots7\\\ \dots4\dots5\dots67\\ \dots4\dots567 \end{split} \]

可以发现的是每一次冒泡,队列里的每一个数,都会移动到它的下一个数的屁股后面。又因为队列里的数的顺序是按照向前移动完的时间加进去的,那么比 \(x\) 大的数一定会先加入队列中。那么对于第一次移动,\(x\)会移动到 第一个大于它的数 \(y\) 的屁股后面,也就是 \(pos_y-1\),第二次移动时,\(y\) 会移动到 \(z\) 的屁股后面,所以 \(pos_y=pos_z-1\),然后 \(x\) 移动到 \(y\) 的后面 \(pos_x=pos_y-1=pos_z-2\)。规律显然。对于第 \(k\) 次移动,在队列里第 \(k\) 个的数的位置为 \(pos_k\),那么答案即为 \(pos_k-k\)。又因为在队列里比他靠前的数只能是比它大的数,那么就在原序列里找从左往右数第 \(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\) 的指数必然其为中位数,操作次数即为 \(\sum|x-x_m|\)

考虑将贡献拆解。将整个序列的指数 \(p\) 提取出来排个序,枚举第 \(i\) 位,考虑第 \(i\) 个数的指数 \(p\) 能给答案做出怎样的贡献。不妨考虑在 \(i\) 左边选择 \(a\) 个数,右边选择 \(b\) 个数。因为是有序的,所以当 \(a>b\) 的时候中位数一定在 \(a\) 里边取得,设中位数为 \(x_m\),那么应有 \(x_i - x_m\),但可以发现的是,中位数的贡献是一定可以拆绝对值之后左右消掉的,所以贡献应为 \(x_i\times \binom{i-1}{a}\times \binom{n-i}{b}\)。复杂度 \(\mathcal{O}(N^3)\)

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

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

posted @ 2024-11-12 19:11  XiaoLe_MC  阅读(22)  评论(0)    收藏  举报