首先明确值域:时和分上上限为
n
−
1
,
m
−
1
n-1,m-1
n−1,m−1.
然后我们求出
n
−
1
,
m
−
1
n-1,m-1
n−1,m−1在
7
7
7进制下的位数,若位数之和大于
7
7
7则直接输出
0
0
0,否则由于值域很小所以暴力状压。
const ll N=1e5+5;
inline ll calc(ll x){
if (x==0) return 1;
ll res=0;
while (x){
++res;
x/=7;
}
return res;
}
ll n, m;
ll a, b;
ll bin[20];
inline ll get_st(ll x, ll y){
ll s=0;
for (R ll i=1; i<=y; i++, x/=7){
if (s&bin[x%7]) return -1;
s|=bin[x%7];
}
return s;
}
ll res;
int main(){
read(n); read(m);
--n; --m;
a=calc(n); b=calc(m);
for (R ll i=0; i<10; i++) bin[i]=(1<<i);
if (a+b>7) return puts("0"), 0;
for (R ll i=0; i<=n; i++)
for (R ll j=0; j<=m; j++){
ll s1=get_st(i, a), s2=get_st(j, b);
if (s1!=-1 && s2!=-1 && ((s1&s2)==0)) ++res;
}
writeln(res);
}
这里树的重心定义稍有不同,指的是删掉这个点余下联通块大小不大于原联通块一半的点.
所以有这么一个性质:一个点的重心一定在其子树重心到它本身的路径上,暴力跳就可以了,反正是 O ( n ) \mathcal{O}(n) O(n)的
const ll N=3e5+5;
ll head[N], to[N<<1], next[N<<1], tot;
inline void add(ll x, ll y){
to[++tot]=y; next[tot]=head[x]; head[x]=tot;
}
inline void Link(ll x, ll y){
add(x, y); add(y, x);
}
ll n, m;
ll g[N];
ll siz[N], mx[N];
ll fa[N];
inline void dfs(ll x){
siz[x]=1;
for (R ll i=head[x], ver; i; i=next[i]){
ver=to[i];
dfs(ver);
siz[x]+=siz[ver];
chkmax(mx[x], siz[ver]);
}
}
inline void dfs1(ll x){
bool flag=false;
for (R ll i=head[x], ver; i; i=next[i]){
flag=true;
ver=to[i];
dfs1(ver);
ll tmp=g[ver];
while (1){
if (max(mx[tmp], siz[x]-siz[tmp])<=siz[x]/2){
g[x]=tmp;
break;
}
if (tmp==x) break;
tmp=fa[tmp];
}
}
if (!flag) g[x]=x;
}
int main(){
read(n); read(m);
for (R ll i=2; i<=n; i++){
read(fa[i]); add(fa[i], i);
}
dfs(1);
dfs1(1);
ll x;
while (m--){
read(x);
writeln(g[x]);
}
}