CSP-S模拟20

A. 归隐

快速幂不开 longlong 炸了

通过观察性质,发现 fn=n+i=0n23i

然后大力推式子,最后推成 [(3n1)/2+n]/2

快速幂即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

ll n;
const int maxn = 100005;
const int mod = 998244353;
const int inv2 = 499122177;
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1) ans = 1ll * ans * x % mod;
	return ans;
}
void calc(ll n){
	ll x = qpow(3, n % (mod - 1)) + mod - 1;
	x = x % mod * inv2 % mod;
	x = (x + n) % mod;
	x = x % mod * inv2 % mod;
	printf("%lld\n",x);
}
int main(){
	freopen("gy.in","r",stdin);
	freopen("gy.out","w",stdout);
	cin >> n;
	calc(n);
	return 0;
}

B. 按位或

不会不会,能推一个性质就是 2imod31/1 而且只与 i 的奇偶性有关

这样处理 3 的倍数的条件就方便多了

考虑进行容斥, fi,j 表示至多 i 个奇数位, j 个偶数位为 1的方案数

暴力枚举具体是几个奇数偶数位,判断其是 3 的倍数就用组合数算出方案

考虑容斥,就是两位可以看成 起来的考虑,也可以理解成二维的什么容斥,不过我的理解方式不太相同。不重要了

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int mod = 998244353;
ll n, t;
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1) ans = 1ll * ans * x % mod;
	return ans;
}
int f[75][75], c[75][75];
int c0, c1;


int main(){
	freopen("or.in","r",stdin);
	freopen("or.out","w",stdout);
	cin >> n >> t;
	for(int i = 0; i <= 70; ++i){
		c[i][0] = 1;
		for(int j = 1; j <= i; ++j)
			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
	}
	for(int i = 0; i <= 60; ++i)if(t & (1ll << i)){
		if(i & 1)++c1; else ++c0;
	}
	for(int i = 0; i <= c1; ++i)
		for(int j = 0; j <= c0; ++j)
			for(int p = 0; p <= i; ++p)
				for(int q = 0; q <= j; ++q)
					if((p - q) % 3 == 0)f[i][j] = (f[i][j] + 1ll * c[i][p] * c[j][q] % mod) % mod;
	int ans = 0;
	n %= (mod - 1);
	for(int i = c1; i >= 0; --i)
		for(int j = c0; j >= 0; --j){
			int opt = (c1 - i + c0 - j) & 1 ? -1 : 1;
			ans = (ans + 1ll * opt * c[c1][i] * c[c0][j] % mod * qpow(f[i][j], n) % mod) % mod;
		}
	ans = (ans % mod + mod) % mod;
	printf("%d\n",ans);
	return 0;
}

C. 最短路径

所以暴力为啥还能打挂啊。。。。。。

经典的期望线性性,先不考虑每次会不走一条直径。

考虑每条边被经过的次数,枚举两侧有多少关键点,用组合数乘出来即可,注意每条边被走两次

然后考虑减掉直径,树的直径有个性质是从根节点出发的最远点是直径的一端

所以按照距离某个点的顺序对关键点排序,按顺序枚举直径的一个端点,这样比他远的点就不在我们考虑范围内了

然后把能够考虑的点按照距离当前端点的距离排序,每次枚举另外一个端点,组合数算出方案即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 2005;
const int mod = 998244353;
int n, m, k;
int head[maxn], tot;
struct edge{int to, net;}e[maxn << 1 | 1];
void link(int u, int v){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
}
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1) ans = 1ll * ans * x % mod;
	return ans;
}
void add(int &x, int y){x += y; x = x >= mod ? x - mod : x;}
int key[maxn], now, dis[maxn][maxn], iskey[maxn];
void get_dis(int x, int fa){
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa)continue;
		dis[now][v] = dis[now][x] + 1;
		get_dis(v, x);
	}
}
int fac[maxn], inv[maxn];
int C(int n, int m){
	// printf("%d %d\n",n, m);
	if(n < 0 || m < 0 || n < m)return 0;
	return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int si[maxn];
int ans;
void dfs(int x, int fa){
	si[x] = iskey[x];
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa)continue;
		dfs(v, x);
		si[x] += si[v];
		for(int j = 1; j <= min(si[v], k - 1); ++j)
			add(ans, 1ll * C(si[v], j) * C(m - si[v], k - j) * 2 % mod);
	}
}
bool cmp(int x, int y){return dis[1][x] > dis[1][y];}
int dist[maxn];
void del(){
	for(int i = 1; i <= m; ++i){
		int x = key[i], p = 0;
		for(int j = i + 1; j <= m; ++j){
			int y = key[j];
			dist[++p] = dis[x][y];
		}
		sort(dist + 1, dist + p + 1);

		for(int j = p; j >= 1; --j)add(ans, mod - 1ll * C(j - 1, k - 2) * dist[j]  % mod);
	}
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n = read(), m = read(), k = read(); 	
	for(int i = 1; i <= m; ++i)iskey[key[i] = read()] = true;
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		link(u, v); link(v, u);
	}
	for(int i = 1; i <= n; ++i){now = i; dis[i][i] = 0; get_dis(i, 0);}
	fac[0] = inv[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1]  * i % mod;
	inv[n] = qpow(fac[n], mod - 2); for(int i = n - 1; i >= 1; --i)inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
	dfs(1, 0);
	sort(key + 1, key + m + 1, cmp);
	// printf("%d\n",ans);
	del();
	ans = 1ll * ans * qpow(C(m, k), mod - 2) % mod;
	printf("%d\n",ans);
	return 0;
}

D. 最短路

之前跟 Delov 做过的大 nb 题,其实就是用主席树优化了高精度,代码过于ex,但是对想练习<主席树>的同学来说是个好题

code
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cmath>
#include<cassert>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 200055;
const int mod = 1e9 + 7;
const int base = 2;
int n, m, head[maxn], tot, mx = 200040;
struct edge{int to, net, val;}e[maxn << 1 | 1];
void link(int u, int v, int w){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
	e[tot].val = w;
}
int base1[maxn];
ull base2[maxn];
struct node{
	int l, r, cnt;
	ull h1, h2;
}t[maxn * 100];
int cnt;
void push_up(int x){
	t[x].h1 = (t[t[x].l].h1 + t[t[x].r].h1) % mod;
	t[x].h2 = t[t[x].l].h2 + t[t[x].r].h2;
	t[x].cnt = t[t[x].l].cnt + t[t[x].r].cnt;
}
void built(int &x, int l, int r){
	if(!x)x = ++cnt;
	if(l == r){
		t[x].h1 = base1[l];
		t[x].h2 = base2[l];
		t[x].cnt = 1;
		return;
	}
	int mid = (l + r) >> 1;
	built(t[x].l, l, mid);
	built(t[x].r, mid + 1, r);
	push_up(x);	
}
int insert(int x, int l, int r, int pos){
	int tmp = ++cnt; t[tmp] = t[x];
	if(l == r){
		t[tmp].h1 = base1[l];
		t[tmp].h2 = base2[l];
		t[tmp].cnt = 1;
		t[tmp].l = t[tmp].r = 0;
		return tmp;
	}
	int mid = (l + r) >> 1;
	if(pos <= mid)t[tmp].l = insert(t[x].l, l, mid, pos);
	else t[tmp].r = insert(t[x].r, mid + 1, r, pos);
	push_up(tmp);
	return tmp;
}
int remove(int x, int l, int r, int L, int R){
	int tmp = ++cnt; t[tmp] = t[x];
	if(L <= l && r <= R){
		t[tmp].h1 = t[tmp].h2 = t[tmp].cnt = 0;
		t[tmp].l = t[tmp].r = 0;
		return tmp;
	}
	int mid = (l + r) >> 1;
	if(L <= mid)t[tmp].l = remove(t[x].l, l, mid, L, R);
	if(R > mid)t[tmp].r = remove(t[x].r, mid + 1, r, L, R);
	push_up(tmp);
	return tmp;
}
bool the_same(int x, int y){
	return t[x].h1 == t[y].h1 && t[x].h2 == t[y].h2 && t[x].cnt == t[y].cnt;
}
bool cmp(int x, int y, int l, int r){
	if(t[x].cnt == 0 || t[y].cnt == 0)return t[x].cnt < t[y].cnt;
	if(l == r){return t[x].cnt < t[y].cnt;}
	int mid = (l + r) >> 1;
	if(the_same(t[x].r, t[y].r))return cmp(t[x].l, t[y].l, l, mid);
	else return cmp(t[x].r, t[y].r, mid + 1, r);
}
bool all(int x, int l, int r, int L, int R){
	if(L <= l && r <= R)return t[x].cnt == (r - l + 1);
	int mid = (l + r) >> 1;
	bool ans = 1;
	if(L <= mid)ans &= all(t[x].l, l, mid, L, R);
	if(R > mid)ans &= all(t[x].r, mid + 1, r, L, R);
	return ans;
}
int bound(int x, int l, int r, int pos){
	if(l == r)return l;
	int mid = (l + r) >> 1;
	if(pos > mid)return bound(t[x].r, mid + 1, r, pos);
	if(all(t[x].l, l, mid, pos, mid))return bound(t[x].r, mid + 1, r, mid + 1);
	return bound(t[x].l, l, mid, pos);
}
int add(int x, int w){
	int pos = bound(x, 0, mx, w);
	int tmp = insert(x, 0, mx, pos);
	if(pos == w)return tmp;
	tmp = remove(tmp, 0, mx, w, pos - 1);
	return tmp;
}
struct lft{
	int ch[maxn][2], dist[maxn], val[maxn], from[maxn];
	int tmp, rt = 0, size;
	int merge(int x, int y){
		if(!x || !y)return x | y;
		if(cmp(val[y], val[x], 0, mx))swap(x, y);
		ch[x][1] = merge(ch[x][1], y);
		if(dist[ch[x][1]] > dist[ch[x][0]])swap(ch[x][1], ch[x][0]);
		dist[x] = dist[ch[x][1]] + 1;
		return x;
	}
	void push(int u, int nrt){
		++size;
		from[++tmp] = u; 
		val[tmp] = nrt;
		rt = merge(tmp, rt);
	}
	void pop(){--size;rt = merge(ch[rt][1], ch[rt][0]);}
	int top(){return from[rt];}
	bool empty(){return size == 0;}
}q;
bool vis[maxn];
int S, T;
int root[maxn], from[maxn];
int num;
void dij(){
	built(root[0], 0, mx);
	for(int i = 1; i <= n; ++i)root[i] = root[0];
	root[S] = ++cnt;
	q.push(S, root[S]);
	while(!q.empty()){
		int x = q.top(); q.pop();
		if(vis[x])continue; vis[x] = 1;
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to; if(vis[v])continue;
			int prt = add(root[x], e[i].val); 
			if(cmp(prt, root[v], 0, mx)){
				root[v] = prt;
				q.push(v, root[v]);
			}
		}
	}
	if(root[T] == root[0]){
		printf("-1\n");
		return;
	}
	printf("%llu\n",t[root[T]].h1);
}
int main(){
	freopen("hellagur.in","r", stdin);
	freopen("hellagur.out","w",stdout);
	n = read(), m = read();
	base1[0] = 1; for(int i = 1; i <= mx; ++i)base1[i] = base1[i - 1] * 2ll % mod;
	base2[0] = 1; for(int i = 1; i <= mx; ++i)base2[i] = base2[i - 1] + base2[i - 1];
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read(), w = read();
		link(u, v, w); link(v, u, w);
	}
	S = read(), T = read();
	dij();
	return 0;
}
posted @   Chen_jr  阅读(53)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示