A. 卡尔的连招

B. 聪明的班主任

C. 一道简单题

D. 又是一道简单题

E. 车轮轴迹

F. Easy Number

A. 卡尔的连招

待更

B. 聪明的班主任

题目大意:有$n$ 个人每人有$a_i$ 本书,每次可以使任意$m$ 个人将他的书送给相邻的一个人,问最少几次能使所有人有相同本书。

贪心

具体思路见代码。

代码如下:

#include <iostream>
#include <cstdio>
using namespace std;
int T,n,a[10005];
int main(void){
    scanf("%d",&T);
    while(T--){
        int sum=0,temp=0,ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        if(sum%n!=0){
            printf("-1\n");
            continue;
        }
        sum/=n;
        for(int i=1;i<=n;++i){
            temp+=sum-a[i];
            ans=max(ans,temp>0?temp:-temp);
            ans=max(ans,a[i]-sum>0?a[i]-sum:sum-a[i]);
        }
        printf("%d\n",ans);
    }
}

C. 一道简单题

待更

D. 又是一道简单题

题目大意:询问以树上两结点$u,v$ 的最近公共祖先为根的子树中,不在这$u,v$ 的简单路径中的最小编号。若无则输出$-1$ 。

LCA+DFS序

首先预处理LCA,复杂度$O(nlgn)$ 。

DFS整棵树,并记录各个结点$x$ 的进入时间$in[x]$ ,出去时间$out[x]$ 以及以$x$ 为根的子树中最小的结点编号$minnode[x]$ 。

当且仅当,$in[x] \leqslant in[y]$ 且$out[y] \leqslant out[x]$ 时,结点$y$ 在以结点$x$ 为根的子树中。

如此一来,对每个询问,只需要遍历以$LCA(u,v)$ 为根的子树,$O(1)$ 的复杂度判定当前结点$x$ 是否位于$u$ 和$v$ 的简单路径中,若不在则直接返回$minnode[x]$ ,单次查询复杂度$O(deep)$ ,其中$deep$ 表示整颗树的深度。

代码如下:

#include <cstdio>
#include <vector>
#include <cmath>
#define N 50010
using namespace std;
int T,minnode[N];
int n,m,u,v,tot,in[N],out[N],ans,father[N];
vector<int>e[N];
struct LCA{
    int k,ver[2*N],dep[2*N],dp[2*N][20],f[N];
    void init(){
        k=0;
    }
    void prepare(int root){
        dfs(root,0,-1);
        rmq(k);
    }
    void dfs(int x,int d,int fa){
        f[x]=k;
        dep[k]=d;
        ver[k++]=x;
        for(int i=0;i<(int)e[x].size();++i)if(e[x][i]!=fa){
            dfs(e[x][i],d+1,x);
            dep[k]=d;
            ver[k++]=x;
        }
    }
    void rmq(int k){
        for(int i=0;i<k;++i)dp[i][0]=i;
        for(int j=1;(1<<j)<=k;++j){
            for(int i=0;i+(1<<j)<=k;++i){
                int x=dp[i][j-1],y=dp[i+(1<<(j-1))][j-1];
                dp[i][j]=dep[x]<dep[y]?x:y;
            }
        }
    }
    int lca(int i,int j){
        int u=f[i],v=f[j];
        if(u>v)swap(u,v);
        int len=log(v-u+1.0)/log(2.0);
        int x=dp[u][len],y=dp[v+1-(1<<len)][len];
        return ver[dep[x]<dep[y]?x:y];
    }
}gao;
void pdfs(int u,int fa){
    in[u]=++tot;
    father[u]=fa;
    minnode[u]=u;
    for(int i=0;i<(int)e[u].size();++i)if(e[u][i]!=fa){
        pdfs(e[u][i],u);
        minnode[u]=min(minnode[u],minnode[e[u][i]]);
    }
    out[u]=tot;
}
void dfs(int x,int u,int v,int fa){
    if((in[x]<=in[u]&&out[u]<=out[x])
       ||(in[x]<=in[v]&&out[v]<=out[x])){
        for(int i=0;i<(int)e[x].size();++i)if(e[x][i]!=fa)
            dfs(e[x][i],u,v,x);
    }else{
        ans=min(ans,minnode[x]);
    }
}
int main(void){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        gao.init();
        for(int i=1;i<n;++i){
            scanf("%d%d",&u,&v);
            e[u].push_back(v);
            e[v].push_back(u);
        }
        gao.prepare(1);
        tot=0;
        pdfs(1,-1);
        while(m--){
            scanf("%d%d",&u,&v);
            int lca=gao.lca(u,v);
            ans=N;
            dfs(lca,u,v,father[lca]);
            if(ans!=N)printf("%d\n",ans);
            else printf("-1\n");
        }
        for(int i=1;i<=n;++i)
            e[i].clear();
    }
}

E. 车轮轴迹

待更

F. Easy Number

题目大意:给出$n$ 个数,定义能被$n$ 个数中任意一个整除的数为Easy Number,问第$k$ 大的Easy Number是什么。

二分+容斥原理

$[0,x]$ 中能被$p$ 整除的数的个数有$\lfloor \frac{x}{p} \rfloor$ 。

由此我们可以利用容斥原理求出$x$ 以内能被任意一个$n$ 个数整除的数的个数,即Easy Number的个数,复杂度为$O(n \times 2^n)$ 。

二分这个界,则可找出第$k$ 大的数。

总复杂度为$O(log_2k \times n \times 2^n)$ 。

代码如下:

#include <cstdio>
using namespace std;
typedef long long ll;
ll T,n,k,a[15];
ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
ll lcm(ll a,ll b){
    return a/gcd(a,b)*b;
}
bool check(ll x){
    ll ans=0;
    for(ll i=0;i<(1<<n);++i){
        ll cnt=0,temp=1;
        for(ll j=0;j<n;++j)if(i&(1<<j)){
            temp=lcm(temp,a[j]);
            cnt++;
        }
        if(cnt)ans+=(cnt&1)?x/temp:-x/temp;
    }
    return ans>=k;
}
int main(void){
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld",&n,&k);
        for(int i=0;i<n;++i)scanf("%lld",&a[i]);
        ll l=1,r=1000000000;
        while(l<r){
            ll mid=(l+r)>>1;
            if(check(mid))r=mid;
            else l=mid+1;
        }
        printf("%lld\n",r);
    }
}