注意
k
k
k 要开
unsigned long long
\text{unsigned long long}
unsigned long long
O
(
n
)
O(n)
O(n)
Code
#include<bits/stdc++.h>typedefunsignedlonglong ull;int n;
ull k;voidsolve(int n, ull k){if(!n)return;
ull mid =1ull<< n -1;if(k < mid)putchar('0'),solve(n -1, k);elseputchar('1'),solve(n -1, mid -1-(k - mid));}intmain(){
std::cin >> n >> k;solve(n, k);returnputs(""),0;}
D1T2 brackets
Solution
令
c
n
t
u
cnt_u
cntu 表示根到
u
u
u 的路径组成的括号序列,以
u
u
u 为右端点的合法括号序列个数
那么
k
u
k_u
ku 就等于根到
u
u
u 的路径上所有点的
c
n
t
cnt
cnt 之和
易得如果存在
u
u
u 的一个深度最大的祖先
v
v
v 使得
v
v
v 到
u
u
u 的路径组成的括号序列是合法括号序列
那么
c
n
t
u
=
c
n
t
f
a
v
+
1
cnt_u=cnt_{fa_v}+1
cntu=cntfav+1
对于求这个
v
v
v ,可以维护一个栈
从根到
u
u
u ,如果是左括号则直接加入,如果是右括号且栈不空则弹栈
那么如果
u
u
u 为右括号,那么
v
v
v 为这次弹出的括号对应的点
而对于求出所有的
u
u
u ,可以在对树
DFS
\text{DFS}
DFS 的过程中维护这个栈,在
DFS
\text{DFS}
DFS 回溯时把栈操作也退回即可
O
(
n
)
O(n)
O(n)
Code
#include<bits/stdc++.h>template<classT>inlinevoidread(T &res){
res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);if(bo) res =~res +1;}typedeflonglong ll;constint N =5e5+5;int n, fa[N], ecnt, nxt[N], adj[N], go[N], stk[N], top, cnt[N];char s[N];
ll sum[N], ans;voidadd_edge(int u,int v){
nxt[++ecnt]= adj[u]; adj[u]= ecnt; go[ecnt]= v;}voiddfs(int u){int tf =0;if(s[u]=='(') stk[++top]= u;elseif(top) cnt[u]= cnt[fa[tf = stk[top--]]]+1;
sum[u]= sum[fa[u]]+ cnt[u];
ans ^= sum[u]* u;for(int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])dfs(v);if(s[u]=='(') top--;elseif(tf) stk[++top]= tf;}intmain(){int x;read(n);scanf("%s", s +1); n =strlen(s +1);for(int i =2; i <= n; i++)read(x),add_edge(fa[i]= x, i);dfs(1);return std::cout << ans << std::endl,0;}
D2T1 meal
Solution
如果没有一半的限制,那么答案为
∏
i
=
1
n
(
1
+
∑
j
=
1
m
a
i
,
j
)
−
1
\prod_{i=1}^n(1+\sum_{j=1}^ma_{i,j})-1
i=1∏n(1+j=1∑mai,j)−1
而出现次数超过一半的主要食材最多
1
1
1 种
故可以枚举超过一半的主要食材是哪种,并把对应的方案数从上式种扣掉即可
假设确定了一种食材
x
x
x ,考虑如何求这种食材出现超过一半的方案数
设
u
i
=
a
i
,
x
u_i=a_{i,x}
ui=ai,x ,
v
i
=
∑
j
=
1
,
j
≠
x
m
a
i
,
j
v_i=\sum_{j=1,j\ne x}^ma_{i,j}
vi=∑j=1,j=xmai,j
问题就转化成了有
n
n
n 个变量,对于第
i
i
i 个变量有
u
i
u_i
ui 种方法使其为
1
1
1 ,
v
i
v_i
vi 种方法使其为
−
1
-1
−1 ,
1
1
1 种方法使其为
0
0
0,求有多少种方案使得
1
1
1 的个数严格大于
−
1
-1
−1 (所有变量的和严格大于
0
0
0 )
于是可以
DP
\text{DP}
DP :设
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示前
i
i
i 个变量和为
j
j
j 的方案数(
j
j
j 可以为负),转移时枚举下一个变量的取值
O
(
m
n
2
)
O(mn^2)
O(mn2)
Code
#include<bits/stdc++.h>template<classT>inlinevoidread(T &res){
res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);if(bo) res =~res +1;}constint N =105, E =205, M =2005, rqy =998244353;int n, m, a[N][M], f[N][E], sum[N], tmp[N], ans =1;inlinevoidadd(int&a,constint&b){
a += b;if(a >= rqy) a -= rqy;}inlinevoidsub(int&a,constint&b){
a -= b;if(a <0) a += rqy;}intmain(){read(n);read(m);for(int i =1; i <= n; i++) sum[i]=1;for(int i =1; i <= n; i++)for(int j =1; j <= m; j++)read(a[i][j]),add(sum[i], a[i][j]);for(int i =1; i <= n; i++) ans =1ll* ans * sum[i]% rqy;sub(ans,1);for(int i =1; i <= m; i++){for(int j =1; j <= n; j++) tmp[j]= sum[j],sub(tmp[j], a[j][i]),sub(tmp[j],1);for(int j =-n; j <= n; j++)for(int k =0; k <= n; k++)
f[k][j + n]=0;
f[0][n]=1;for(int j =1; j <= n; j++)for(int k =-n; k <= n; k++){add(f[j][k + n], f[j -1][k + n]);if(k >-n)add(f[j][k + n],1ll* f[j -1][k -1+ n]* a[j][i]% rqy);if(k < n)add(f[j][k + n],1ll* f[j -1][k +1+ n]* tmp[j]% rqy);}for(int j =1; j <= n; j++)sub(ans, f[n][j + n]);}return std::cout << ans << std::endl,0;}