给一棵树 (
n
≤
1
0
5
n \le 10^5
n≤105),每个节点有一个权值。然后给
m
m
m 个区间
m
≤
1
0
5
m \le 10^5
m≤105,以及这个区间内的一个结点。然后问每个点的权值,在这个区间内,并且存在他的一个子结点或者父结点,使得其权值也在这个区间内。还有一个条件,就是子节点的权值小于等于父结点
看一遍思路,就明白了
首先预处理出树上倍增的数组
f
(
u
,
k
)
f(u, k)
f(u,k),即
u
u
u 的
2
k
2^k
2k 的结点是哪个.
然后对于每一个区间内的结点,找到小于等于区间右端点
r
r
r 的权值最大的父结点u. 把当前的 L 推入到
v
e
c
[
u
]
vec[u]
vec[u] 中(vector数组)
然后
d
f
s
dfs
dfs 一遍,建立一个权值树状数组,把每一个
v
e
c
[
u
]
vec[u]
vec[u] 中的左端点对用的树状数组的位置加1. 然后当前的
a
n
s
[
u
]
=
s
u
m
(
a
g
e
[
u
]
)
ans[u] = sum(age[u])
ans[u]=sum(age[u])
深搜下去。然后回溯,把
v
e
c
[
u
]
vec[u]
vec[u] 中的每一个左端点在减掉
这样子,就相当于把以 u 为根节点的子树都左端点都加进去,看看当前有多少个 l 满足
l
<
=
a
g
e
[
u
]
l <= age[u]
l<=age[u].
#include<bits/stdc++.h>usingnamespace std;constint N =100010;int tr[N], age[N];
vector<int> vec[N];int h[N], e[N], ne[N], idx;int fa[N][20];int ans[N];intlowbit(int x){return x &-x;}voidinsert(int x,int c){for(int i = x; i <=100000; i +=lowbit(i)){
tr[i]+= c;}}intsum(int x){int res =0;for(int i = x; i; i -=lowbit(i)){
res += tr[i];}return res;}voidadd(int a,int b){
e[idx]= b, ne[idx]= h[a], h[a]= idx++;}voidinit(){
queue<int> que;//别忘把根结点 push 进去
que.push(1);while(que.size()){int u = que.front(); que.pop();for(int i = h[u]; i !=-1; i = ne[i]){int v = e[i];
que.push(v);
fa[v][0]= u;for(int k =1; k <=17; k++) fa[v][k]= fa[fa[v][k -1]][k -1];}}}voiddfs(int u){for(auto p : vec[u]){insert(p,1);}
ans[u]=sum(age[u]);for(int i = h[u]; i !=-1; i = ne[i]){int v = e[i];dfs(v);}for(auto p : vec[u]){insert(p,-1);}}intmain(){memset(h,-1,sizeof h);int n, m;scanf("%d%d",&n,&m);for(int i =1; i <= n; i++){scanf("%d",&age[i]);int p;scanf("%d",&p);if(i !=1)add(p, i);}
age[0]=1e9;init();while(m--){int oj, lj, rj;scanf("%d%d%d",&oj,&lj,&rj);int u = oj;for(int k =17; k >=0; k--){if(age[fa[u][k]]<= rj){
u = fa[u][k];}}
vec[u].push_back(lj);}dfs(1);for(int i =1; i <= n; i++){printf("%d ", ans[i]);}printf("\n");return0;}
H. SBC’s Hangar
题意:给
n
n
n 个数字 (
n
≤
50
n \le 50
n≤50),从中选择 k 个数,使得他们之和在
[
A
,
B
]
[A, B]
[A,B] 之间。这 n 个数中,任意两个数,一定会有一个数至少是另一个数的两倍。求方案数。
方案数应该是凑成
[
0
,
B
]
[0,B]
[0,B] 的方案数减去
[
0
,
A
−
1
]
[0,A-1]
[0,A−1] 的方案数
#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;constint N =60;
ll f[N][N], a[N];int w[N];int n, k;
ll dfs(int pos,int k,int lim){if(pos ==0){return k ==0;}if(lim ==0&& f[pos][k]!=-1){return f[pos][k];}else{
ll ans =0, up = lim ? w[pos]:1;for(int i =0; i <= up; i++){if(k >0) ans +=dfs(pos -1, i ==0? k : k -1, lim && i == up);if(!k &&!i) ans +=dfs(pos -1, k, lim && i == up);}if(lim ==0){
f[pos][k]= ans;}return ans;}}
ll solve(ll x){memset(f,-1,sizeof f);memset(w,0,sizeof w);for(int i =1; i <= n; i++){if(x >= a[i]){
w[n - i +1]=1;
x -= a[i];}}returndfs(n, k,1);}intmain(){
ll A, B;scanf("%d%d",&n,&k);for(int i =1; i <= n; i++)scanf("%lld",&a[i]);scanf("%lld%lld",&A,&B);sort(a +1, a + n +1, greater<ll>());printf("%lld\n",solve(B)-solve(A -1));return0;}
I. Interactivity
题意:给一个树,父结点的权值是子结点权值之和。问最少需要多少询问可以知道树的所有权值。
记
f
(
u
,
0
)
f(u, 0)
f(u,0) 为当前不需要选择这个点,这个子树的权值确定,为
f
(
u
,
0
)
=
∏
v
(
f
[
v
]
[
0
]
+
f
[
v
]
[
1
]
)
f(u, 0) = \prod_v (f[v][0] + f[v][1])
f(u,0)=∏v(f[v][0]+f[v][1]).
记
f
(
u
,
1
)
f(u, 1)
f(u,1) 为当前需要选择这个结点,这个子树的权值才确定下来,那么这个等价于有一个子树也是如此,即不选择这个结点(可以注意到,选择当前子结点的才能确定这个树的权值,和不选择当前点确定不了当前树的权值,是一个意思,方案数是一样的),那么
f
(
u
,
1
)
=
∏
v
f
(
v
,
1
)
∗
∏
v
′
v
′
≠
v
g
(
v
′
)
f(u, 1) = \prod_v f(v, 1) * \prod\limits_{v'}^{v' \ne v} g(v')
f(u,1)=∏vf(v,1)∗v′∏v′=vg(v′).
g
(
u
)
=
f
(
u
,
0
)
+
f
(
u
,
1
)
g(u) = f(u, 0) + f(u, 1)
g(u)=f(u,0)+f(u,1). 表示确定当前子树权值的方案总数。
最后一个求
f
(
u
,
1
)
f(u,1)
f(u,1) 的时候,求那个
g
(
v
′
)
g(v')
g(v′),不可以总的乘积除以 g(v) 的方式,因为当前的 g(v) 可能取模后为0. 可以正着打一个前缀积,倒着打一个前缀积。
#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;const ll mod =1e9+7;constint N =100010;
vector<int> son[N];
ll g[N], f[N][2];
ll pre[N], suf[N];voiddfs(int u){int sz = son[u].size();if(sz ==0){
f[u][1]= g[u]=1;return;}for(int i =0; i < sz; i++){int v = son[u][i];dfs(v);}//这个地方千万不要初始化。
f[u][0]=1;for(int i =0; i < sz; i++){int v = son[u][i];
f[u][0]= f[u][0]* g[v]% mod;}
pre[0]=1, suf[sz +1]=1;for(int i =1; i <= sz; i++){int v = son[u][i -1];
pre[i]= pre[i -1]* g[v]% mod;}for(int i = sz; i >=1; i--){int v = son[u][i -1];
suf[i]= suf[i +1]* g[v]% mod;}for(int i =1; i <= sz; i++){int v = son[u][i -1];
f[u][1]=(f[u][1]+ f[v][1]* pre[i -1]% mod * suf[i +1]% mod)% mod;}
g[u]=(f[u][1]+ f[u][0])% mod;}intmain(){int n;scanf("%d",&n);for(int i =2; i <= n; i++){int x;scanf("%d",&x);
son[x].push_back(i);}dfs(1);printf("%lld\n", g[1]);return0;}
K. Between Us
题意:给 n 个点的图
n
≤
100
n \le 100
n≤100,把这个图分成两部分,每一个点的度数都是奇数。问是否存在这个划分方案。
#include<bits/stdc++.h>usingnamespace std;constint N =110;
bitset<N> a[N];int g[N][N], d[N];int n, m;boolgauss(){int r, c;for(c =1, r =1; c <= n; c++){int t = r;for(int i = r; i <= n; i++){if(a[i][c]){
t = i;break;}}if(a[t][c]==0)continue;swap(a[t], a[r]);for(int i =1; i <= n; i++){if(a[i][c]&& i != r){
a[i]^= a[r];}}
r++;}if(r <= n){for(int i = r; i <= n; i++){if(a[i][n +1])returnfalse;}}returntrue;}intmain(){scanf("%d%d",&n,&m);for(int i =1; i <= m; i++){int a, b;scanf("%d%d",&a,&b);
d[a]++, d[b]++;
g[a][b]= g[b][a]=1;}for(int i =1; i <= n; i++){for(int j =1; j <= n; j++){if(g[i][j]) a[i][j]=1;}if(d[i]&1){
a[i][n +1]=0, a[i][i]=1;}else{
a[i][n +1]=1;}}if(gauss()){printf("Y\n");}elseprintf("N\n");}