Link(需报名看题)
比赛小记
我过了几个签到,牛逼题(B、C、E)都是学长过的(orz学长)
J 莫名其妙(因为精度?)wa了好几发,这把只贡献罚时了属于是
B. Sample Game
题意
有 (n) 个数和一个空数列,每次向数列中随机添加一个数,添加数 (i) 的概率为 (p_i),当数列不是单调不降时停止操作。求数列长度的平方的期望。
(2le nle 100)。
Solution
orz学长
类比 bzoj osu! 的做法,我们可以考虑把 (x^2) 拆开,令 (f_{i,0/1/2}) 分别表示到 (i) 的概率;到 (i) 的长度的期望和到 (i) 的长度平方的期望。可得:
移项一下即可得到递推式,统计答案相当于再加上一个 (lt i) 的点,直接做即可。
code
(by 学长)
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define Rep(i, n) for (int i = 1; i <= n; i ++)
#define Rep0(i, n) for (int i = 0; i <= n; i ++)
#define RepG(i, x) for (int i = head[x]; i; i = edge[i].to)
#define v edge[i].to
#define mp(a, b) make_pair(a, b)
#define fi first
#define se second
typedef double D;
typedef long long LL;
const int mod = 998244353;
LL p[110], f[110][3];
LL qpow(LL a, LL b)
{
LL ret = 1, base = a;
while (b) {
if (b & 1) ret = ret * base % mod;
base = base * base % mod, b >>= 1;
}
return ret;
}
int main()
{
int n, sum = 0;
scanf("%d", &n);
Rep(i, n) { scanf("%d", &p[i]); sum += p[i];}
sum = qpow(sum, mod - 2);
Rep(i, n) p[i] = p[i] * sum % mod;
LL s0 = 1, s1 = 0, s2 = 0;
f[0][0] = 1;
Rep(i, n) {
LL tmp = qpow(1 - p[i] + mod, mod - 2);
f[i][0] = s0 * p[i] % mod * tmp % mod;
s0 = (s0 + f[i][0]) % mod;
f[i][1] = (s0 + s1) * p[i] % mod * tmp % mod;
s1 = (s1 + f[i][1]) % mod;
f[i][2] = (s2 + s0 + s1 * 2) * p[i] % mod * tmp % mod;
s2 = (s2 + f[i][2]) % mod;
}
LL sp = 0, ans = 0;
Rep(i, n) {
LL tmp = sp * (f[i][2] + f[i][0] + f[i][1] * 2) % mod;
ans = (ans + tmp) % mod, sp = (sp + p[i]) % mod;
}
printf("%lld
", ans);
return 0;
}
E. Tree Xor
Solution
显然题意可以转化成求满足 (x_i~mathrm{xor}~w'_iin[l_i,r_i]) 的 (x) 的个数(这里 (w_i) 为当前点到根的 (w_i) 异或和)。
考虑 ([l_i,r_i]) 一定能拆分成 ( ext{trie}) 树上 (log) 段区间,而 ( ext{xor}) 操作相当于交换 ( ext{trie}) 树上若干子树,不会影响区间数量。我们对于异或可以在根节点打上标记,然后将 ([l_i,r_i]) 直接插入 ( ext{trie}) 树即可。
code
#include<bits/stdc++.h>
using namespace std;
inline int gi()
{
char c=getchar(); int x=0;
for(;c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
return x;
}
const int N=3e5+5;
int n,l[N],r[N],w[N],x[N],ch[N*30][2],tag[N*30],add[N*30],tot=1,ans;
vector<pair<int,int>> e[N];
void dfs(int u, int fa)
{
for(auto x:e[u]) if(x.first!=fa)
{
w[x.first]=w[u]^x.second;
dfs(x.first,u);
}
}
void pushdown(int u, int i, bool f)
{
if(!ch[u][0]&&f) ch[u][0]=++tot;
if(!ch[u][1]&&f) ch[u][1]=++tot;
if(!tag[u]) return ;
if(tag[u]&(1<<i)) swap(ch[u][0],ch[u][1]);
tag[ch[u][0]]^=tag[u],tag[ch[u][1]]^=tag[u];
tag[u]=0;
}
void trie_ins(int l, int r, int u=1, int x=0, int i=29)
{
if(i<0)
{
++add[u];
return ;
}
pushdown(u,i,1);
if(l<=x&&x+(1<<i+1)-1<=r)
{
++add[u];
return ;
}
if(x+(1<<i)-1>=l) trie_ins(l,r,ch[u][0],x,i-1);
if(x+(1<<i)<=r) trie_ins(l,r,ch[u][1],x|(1<<i),i-1);
}
void trie_dfs(int u=1, int sum=0, int i=29)
{
if(!u) return ;
pushdown(u,i,0);
sum+=add[u];
if(sum==n)
{
ans+=(1<<i+1);
return ;
}
if(i<0) return ;
trie_dfs(ch[u][0],sum,i-1);
trie_dfs(ch[u][1],sum,i-1);
}
int main()
{
n=gi();
for(int i=1;i<=n;++i) l[i]=gi(),r[i]=gi();
for(int i=1;i<n;++i)
{
int u=gi(),v=gi(),w=gi();
e[u].push_back(make_pair(v,w)),e[v].push_back(make_pair(u,w));
}
dfs(1,0);
for(int i=1;i<=n;++i)
{
tag[1]^=w[i];
trie_ins(l[i],r[i]);
tag[1]^=w[i];
}
trie_dfs();
printf("%d
",ans);
}
G. Product
题意
给定 (n,k,D),定义数列 ({a_n}) 的价值为
求所有满足以下条件的数列 ({a_n}) 的价值之和:
- (forall iin[1,n],a_ige 0)
- (sum_{i=1}^n a_i=D)
对 (998244353) 取模。
(1le nle 50),(0le kle 50),(0le Dle 10^8)。
Solution
考虑 (k=0) 下该式的组合意义,考虑有一个长度为 (D) 的序列,而 (a_i) 为 (i) 出现的次数,那么 (sum_{a_ige 0,sum a_i=D}frac{D!}{prod_{i=1}^n a_i!}) 即为值域为 ([1,n]) 的长度为 (D) 的数列个数(缩排),即为 (n^D)。
我们直接令 (a_i) 加上 (k) ,题意转化为求 (sum_{a_ige k,sum a_i=D+nk}frac{D!}{prod_{i=1}^n a_i!})。若无 (a_ige k) 的限制,答案即为 (frac{D!}{(D+nk)!}n^{D+nk})。对于限制,我们考虑容斥:令 (x) 个 (a_ilt k) ,这些 (frac{1}{a_i!}) 的贡献可以用 (O(n^2k^2)) 的背包求出;剩余 (n-x) 个数的贡献即为 (frac{D!}{(D+nk-s)!}n^{D+nk-s}) ((s) 为 打破限制的 (x) 个数之和),乘起来再乘上容斥系数即可。
code
#include<bits/stdc++.h>
using namespace std;
const int N=55,M=N*N,Mod=998244353;
int n,k,d,f[N][M],fac[M],inv[M],ans;
#define mul(x,y) (1ll*(x)*(y)%Mod)
inline int add(int x, int y)
{ return (x+y>=Mod?x+y-Mod:x+y);
}
inline int sub(int x, int y)
{ return (x-y<0?x-y+Mod:x-y);
}
inline void upd(int& x, int y)
{ x=(x+y>=Mod?x+y-Mod:x+y);
}
inline int po(int x, int y)
{
int r=1;
while(y)
{
if(y&1) r=mul(r,x);
x=mul(x,x), y>>=1;
}
return r;
}
inline int C(int x, int y)
{
return mul(fac[x],mul(inv[y],inv[x-y]));
}
int calcd(int delta)
{
int r=1;
for(int i=d+1;i<=d+delta;++i) r=mul(r,i);
return po(r,Mod-2);
}
int main()
{
scanf("%d%d%d",&n,&k,&d);
fac[0]=inv[0]=1;
const int m=max(n,n*(k-1));
for(int i=1;i<=m;++i) fac[i]=mul(fac[i-1],i);
inv[m]=po(fac[m],Mod-2);
for(int i=m-1;i>0;--i) inv[i]=mul(inv[i+1],i+1);
f[0][0]=1;
for(int i=1;i<=n;++i)
for(int j=0;j<=(i-1)*(k-1);++j)
for(int l=0;l<k;++l)
upd(f[i][j+l],mul(f[i-1][j],inv[l]));
for(int i=0;i<=n;++i)
{
for(int j=0;j<=i*(k-1);++j)
{
const int delta=n*k-j;
int tmp=mul(f[i][j],mul(po(n-i,d+delta),calcd(delta)));
if(i&1) ans=sub(ans,mul(tmp,C(n,i)));
else ans=add(ans,mul(tmp,C(n,i)));
}
}
printf("%d
",ans);
}