CSP-S模拟20
A. 归隐
快速幂不开 炸了
通过观察性质,发现
然后大力推式子,最后推成
快速幂即可
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. 按位或
不会不会,能推一个性质就是 而且只与 的奇偶性有关
这样处理 的倍数的条件就方便多了
考虑进行容斥, 表示至多 个奇数位, 个偶数位为 的方案数
暴力枚举具体是几个奇数偶数位,判断其是 的倍数就用组合数算出方案
考虑容斥,就是两位可以看成 起来的考虑,也可以理解成二维的什么容斥,不过我的理解方式不太相同。不重要了
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. 最短路
之前跟 做过的大 题,其实就是用主席树优化了高精度,代码过于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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】