比赛链接:周练2
乍一看可能有点问题,实际上仔细想一下就发现这两个题都是把一些简单的思维与算法结合了一下。
D:
7e5的规模的树,边权都为1,点权都为0。
7e5次操作,每次选择一个点,将所有与他距离不超过2的点的点权都加1,然后询问当前点的点权为多少。
将这个树变成有根树后,距离不超过2,有两种情况:
- x向他的子树走1步,或2步
那么很显然把点都映射到一个bfs序列,记录每个节点的第一个儿子,最后一个儿子,他儿子的第一个儿子,他儿子的第二个儿子。
把第一个儿子到最后一个儿子区间内点权加1;儿子的第一个儿子,儿子的最后一个儿子点权加1,就可以把子树中应该加的点加到。 - 通过他的父亲走到他的兄弟节点,或者父亲的父亲
父亲的第一个儿子,最后一个儿子之间的点权加1,父亲点权加1,父亲的父亲点权加1。
于是这个题就解决了,至于为什么要算儿子的儿子,是因为有这种情况:
不是x的每个儿子都有儿子。
于是先bfs一次记录每个点在bfs序列中的位置,再dfs一次算出每个点要维护的儿子信息,以及父亲是谁,之后的修改操作用树状数组做即可。
#include<bits/stdc++.h>
using namespace std;
#define pb(x) push_back(x)
typedef long long ll;
const int maxn=750009;
vector<int> G[maxn];
void add_edge(int u,int v){
G[u].pb(v);
G[v].pb(u);
}
int fa[maxn];
int fson[maxn],sson[maxn];//第一个儿子,最后一个儿子;
int pos[maxn];
int num=0;
int ffson[maxn],ssson[maxn];//儿子的第一个儿子,最后一个儿子;
queue<int> q;
bool f[maxn];
void bfs(){
q.push(1);
while(q.size()){
int u=q.front(); q.pop();
f[u]=1;
pos[u]=++num;
for(auto v:G[u]){
if(f[v]) continue;
q.push(v);
}
}
}
void dfs(int u,int last){
fa[u]=last;
for(auto v:G[u]){
if(v==last) continue;
if(!fson[u]) fson[u]=sson[u]=v;
else sson[u]=v;
dfs(v,u);
if(!ffson[u]) ffson[u]=fson[v],ssson[u]=sson[v];
else if(sson[v]) ssson[u]=sson[v];
}
}
int n;
int sum[maxn];
void add(int x,int y){
for(;x<=n;x+=x&-x) sum[x]+=y;
}
int ask(int x){
int res=0;for(;x;x-=x&-x) res+=sum[x];return res;
}
void upd(int x){
if(x==1) add(pos[x],1),add(pos[x]+1,-1);//根节点;
int F=fson[x],S=sson[x];
if(F) add(pos[F],1),add(pos[S]+1,-1);
int ff=ffson[x],ss=ssson[x];
if(ff) add(pos[ff],1),add(pos[ss]+1,-1);
int Fa=fa[x];
if(Fa){
ff=fson[Fa],ss=sson[Fa];
add(pos[ff],1),add(pos[ss]+1,-1);
add(pos[Fa],1),add(pos[Fa]+1,-1);
if(fa[Fa]) add(pos[fa[Fa]],1),add(pos[fa[Fa]]+1,-1);
}
}
int main(){
int q,u,v,x;
cin>>n>>q;
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
add_edge(u,v);
}
bfs();
dfs(1,0);
while(q--){
scanf("%d",&x);
upd(x);
printf("%d\n",ask(pos[x]));
}
return 0;
}
/*
10 100
1 2
1 3
1 4
1 5
2 6
2 7
2 8
3 9
3 10
*/
E:
x<=1e18,2<=m<=100。
第一个式子见过很多次了,f是斐波那契数列,于是打表算出1e18以内的所有斐波那契数列,先检查有没有f(k)=x,若有则算第一步,没有则算第二步。
- 算x的阶乘在m进制下的末尾0的个数,那么只需要看1~x中的所有数可以凑出多少个m就行了,只需要对m进行质因数分解,分解成 p 1 q 1 ∗ p 2 q 2 . . . p_1^{q_1}*p_2^{q_2}... p1q1∗p2q2...的形式即可,算x!中 p i p_i pi的数量,就是求: n u m = ∑ j = 1 l o g p i x x / p i j num=\sum_{j=1}^{log_{p_i}^x}x/{p_i^j} num=∑j=1logpixx/pij,显然很好算,最后有多少个m就是 m i n ( n u m i / q i ) min(num_i/q_i) min(numi/qi)。
- 皇后最多13个,直接打表算出来存下来就行。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<ll,bool> m;
ll f[1109];
vector<int> a;
vector<int> b;
int hh[109]={0,1,0,0,2,10,4,40,92,352,724,2680,14200,73712};
ll getval(ll x,ll n){
ll res=0;
for(ll p=n;p<=x;p*=n){
res+=x/p;
if(p>x*1.0/n) break;
}
return res;
}
int main(){
f[1]=1,f[2]=1;
m[1]=1;
for(int i=3;i<=87;++i){
f[i]=f[i-1]+f[i-2];
m[f[i]]=1;
//if(f[i]>1e18||f[i]>f[i]+f[i-1]) break;
//cout<<f[i]<<" ";
}
//cout<<endl;
ll x;
int n;
cin>>x>>n;
if(m[x]==1){
for(int i=2;i<=sqrt(n);++i)
if(n%i==0){
int num=0;
while(n%i==0) n/=i,++num;
a.push_back(i),b.push_back(num);
}
if(n>1) a.push_back(n),b.push_back(1);
ll res=getval(x,a[0])/b[0];
for(int i=1;i<a.size();++i)
res=min(res,getval(x,a[i])/b[i]);
cout<<res<<endl;
}
else{
int z=(x%min(13,n))+1;
cout<<hh[z]<<endl;
}
return 0;
}