Codeforces Round #520 (Div. 2)

D. Fun with Integers
题意:
给你一个n,对于任意的 2|a|,|b|n ,如果存在一个整数x,使得 $a*x=b$ 或者 $b*x=a$ ,那么a向b连一条边权为|x|的边。
问不经过重复的边最长的一条路径的长度。
思路:
我们发现这张图有欧拉回路,然后就把所有边权加起来就好了2333333
//By SiriusRen
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,ans;
signed main(){
    scanf("%lld",&n);
    for(int i=2;i<=n;i++)
        for(int j=i*2;j<=n;j+=i)
            ans+=j/i;
    printf("%lld\n",ans*4);
}

E. Company

题意:

给你一个含n个点的有根树,q次询问,每次问[l,r]区间中删掉任何一个点,使得所有点的deep[LCA]最大,问删掉的点是哪个,深度最大是多少。SPJ

思路:

我们可以发现,一段区间的LCA也就是dfs序最小的点和dfs序最大的点的lca。

那我们可以枚举删除的是dfs序最小的那个点还是dfs序最大的那个点。

具体来说就是线段树维护区间dfs序的最小值,次小值,最大值,次大值。每回询问的时候查询一下lca就好了

//By SiriusRen
#include <bits/stdc++.h>
using namespace std;
const int N=200050;
int n,q,fa[N][20],v[N],nxt[N],first[N],tot,dfn[N],rev[N],cnt,deep[N];
int maxx[N<<2],max2[N<<2],minn[N<<2],min2[N<<2];
void add(int x,int y){
    v[tot]=y,nxt[tot]=first[x],first[x]=tot++;
}

void dfs(int x){
    dfn[x]=++cnt,rev[cnt]=x;
    for(int i=1;i<20;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=first[x];~i;i=nxt[i])
        deep[v[i]]=deep[x]+1,dfs(v[i]);
}

void push_up(int pos){
    int lson=pos<<1,rson=pos<<1|1,tmp[4];
    tmp[0]=maxx[lson],tmp[1]=max2[lson];
    tmp[2]=maxx[rson],tmp[3]=max2[rson];
    sort(tmp,tmp+4);
    maxx[pos]=tmp[3],max2[pos]=tmp[2];
    tmp[0]=minn[lson],tmp[1]=min2[lson];
    tmp[2]=minn[rson],tmp[3]=min2[rson];
    sort(tmp,tmp+4);
    minn[pos]=tmp[0],min2[pos]=tmp[1];
}

void build(int l,int r,int pos){
    if(l==r){
        maxx[pos]=minn[pos]=dfn[l];
        min2[pos]=N;
        return;
    }
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    build(l,mid,lson),build(mid+1,r,rson);
    push_up(pos);
}

pair<int,int> query_max(int l,int r,int pos,int L,int R){
    if(l>=L&&r<=R)return make_pair(maxx[pos],max2[pos]);
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    if(mid<L)return query_max(mid+1,r,rson,L,R);
    else if(mid>=R)return query_max(l,mid,lson,L,R);
    else{
        pair<int,int>t=query_max(l,mid,lson,L,R);
        pair<int,int>t2=query_max(mid+1,r,rson,L,R);
        int tmp[4];
        tmp[0]=t.first,tmp[1]=t.second;
        tmp[2]=t2.first,tmp[3]=t2.second;
        sort(tmp,tmp+4);
        return make_pair(tmp[3],tmp[2]);
    }
}

pair<int,int> query_min(int l,int r,int pos,int L,int R){
    if(l>=L&&r<=R)return make_pair(minn[pos],min2[pos]);
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    if(mid<L)return query_min(mid+1,r,rson,L,R);
    else if(mid>=R)return query_min(l,mid,lson,L,R);
    else{
        pair<int,int>t=query_min(l,mid,lson,L,R);
        pair<int,int>t2=query_min(mid+1,r,rson,L,R);
        int tmp[4];
        tmp[0]=t.first,tmp[1]=t.second;
        tmp[2]=t2.first,tmp[3]=t2.second;
        sort(tmp,tmp+4);
        return make_pair(tmp[0],tmp[1]);
    }
}

int lca(int x,int y){
    if(x<1||x>n)return -1;
    if(deep[x]<deep[y])swap(x,y);
    for(int i=19;~i;i--)if(deep[fa[x][i]]>=deep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=19;~i;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

int main(){
    memset(first,-1,sizeof(first));
    scanf("%d%d",&n,&q);
    for(int i=2;i<=n;i++)scanf("%d",&fa[i][0]),add(fa[i][0],i);
    dfs(1);build(1,n,1);
    while(q--){
        int xx,yy;
        scanf("%d%d",&xx,&yy);
        pair<int,int>mx=query_max(1,n,1,xx,yy);
        pair<int,int>mn=query_min(1,n,1,xx,yy);
        int t1=lca(rev[mx.first],rev[mn.second]);
        int t2=lca(rev[mx.second],rev[mn.first]);
        if(deep[t1]>=deep[t2])printf("%d %d\n",rev[mn.first],deep[t1]);
        else printf("%d %d\n",rev[mx.first],deep[t2]);
    }
}

F.

题意:

给你一个DAG。

对于每个点,我们需要统计两个量:

从它出发能到的点的数量a

从其它点出发能到达它的点的数量b

如果a+b=n-2,那么这个点是好的

问这张图里有多少个好的点

思路:

一开始没有思路,参考了题解

我们可以拓扑排序一下,有三种情况

1.拓扑排序的过程中队列大小为0,也就是只有一条链,那么这个点贡献+=还没有进入队列的所有点

2.如果队列大小为1,也就是有两条链,这个时候需要判断一下另一个点所到达的所有点的入读是不是都>1,如果有不是的,那么就不管。

如果都>1,当前点贡献+=还没有进入队列的点

3.如果队列大小>1,那么这个点不可能满足条件

反向建图再来一遍

如果两次贡献和+2>=n,那么这个点是好的

//By SiriusRen
#include <bits/stdc++.h>
using namespace std;
const int N=600050;
int n,m,fm[N],to[N],in[N],all,ans[N],e;
vector<int>g[N];queue<int>q;
void topsort(int *u,int *v){
    memset(in,0,sizeof(in)),all=n;
    for(int i=1;i<=n;i++)g[i].clear();
    while(!q.empty())q.pop();
    for(int i=1;i<=m;i++)g[u[i]].push_back(v[i]),in[v[i]]++;
    for(int i=1;i<=n;i++)if(!in[i])q.push(i),all--;
    while(!q.empty()){
        int t=q.front();q.pop();
        if(q.empty())ans[t]+=all;
        else if(q.size()==1){
            bool flg=1;
            for(auto i:g[q.front()])flg&=(in[i]>1);
            ans[t]+=flg*all;
        }
        else ans[t]=-N;
        for(auto i:g[t])if(!(--in[i]))q.push(i),all--;
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)scanf("%d%d",&fm[i],&to[i]);
    topsort(fm,to),topsort(to,fm);
    for(int i=1;i<=n;i++)if(ans[i]+2>=n)e++;
    printf("%d\n",e);
}

猜你喜欢

转载自www.cnblogs.com/SiriusRen/p/10009636.html