牛客算法周周练2 D E

本文详细解析了一道涉及树状数组和深度优先搜索(DFS)的算法题,通过将树转化为有根树并利用BFS序列优化操作,实现了对特定树形结构中点权的高效更新与查询。同时,另一题探讨了斐波那契数列、阶乘和皇后问题,展示了数学与算法的巧妙结合。
摘要由CSDN通过智能技术生成

比赛链接:周练2
乍一看可能有点问题,实际上仔细想一下就发现这两个题都是把一些简单的思维与算法结合了一下。
D:
7e5的规模的树,边权都为1,点权都为0。
7e5次操作,每次选择一个点,将所有与他距离不超过2的点的点权都加1,然后询问当前点的点权为多少。

将这个树变成有根树后,距离不超过2,有两种情况:

  1. x向他的子树走1步,或2步
    那么很显然把点都映射到一个bfs序列,记录每个节点的第一个儿子,最后一个儿子,他儿子的第一个儿子,他儿子的第二个儿子。
    把第一个儿子到最后一个儿子区间内点权加1;儿子的第一个儿子,儿子的最后一个儿子点权加1,就可以把子树中应该加的点加到。
  2. 通过他的父亲走到他的兄弟节点,或者父亲的父亲
    父亲的第一个儿子,最后一个儿子之间的点权加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,若有则算第一步,没有则算第二步。

  1. 算x的阶乘在m进制下的末尾0的个数,那么只需要看1~x中的所有数可以凑出多少个m就行了,只需要对m进行质因数分解,分解成 p 1 q 1 ∗ p 2 q 2 . . . p_1^{q_1}*p_2^{q_2}... p1q1p2q2...的形式即可,算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)
  2. 皇后最多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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值