• [4.2福建四校联考]


    来自FallDream的博客,未经允许,请勿转载,谢谢。

    ----------------------------------------------------------------------------------------

    我写完A题拍不出错,感觉没问题,交上去全RE,发现我竟然在倍增的时候把0的父亲写成了-1   脑抽爆0了..   本来可以300的

    ----------------------------------------------------------------------------------------

    A.summing

    你有一棵以 你有一棵以 0为根,n个节点的树  n<=500000
    定义:点u的等级为点u到根的距离,树的高度h为树上所有点的等级中最大值F(l,k)表示子树中等级为l的点数量不小于k的点的数量
    现在给你 现在给你 h+1个常数 a0,a1,a2,…,ah,请你求出 ,请你求出 ,请你求出 $sum_{i=0}^{h}F(i,ai)$

    题解:我们发现答案只会在相邻两个点的lca处变化,所以可以把同一个等级的点全部拿出来,然后把它们lca的深度塞进一个pq,每次更新答案就可以了。合并次数最多n次,复杂度nlogn

    我脑抽,改了就过了。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<vector>
    #define ll long long
    #define MN 500000
    #define MK 20
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    
    struct eve{
        int dep,x,y;
        bool operator<(const eve&b)const{return dep<b.dep;}
    };
    priority_queue<eve> q;
    ll ans=0,now=0;
    int n,h,fa[MN+5][MK+5],cnt=0,head[MN+5],a[MN+5],s[MN+5],dep[MN+5],size[MN+5];
    struct edge{int to,next;}e[MN*2+5];
    vector<int> v[MN+5];
    
    void ins(int f,int t){
        e[++cnt]=(edge){t,head[f]};head[f]=cnt;
        e[++cnt]=(edge){f,head[t]};head[t]=cnt;
    }
    
    void dfs(int x,int f)
    {
        v[dep[x]].push_back(x);fa[x][0]=f;
        for(int i=head[x];i;i=e[i].next)
            if(e[i].to!=f)
            {
                dep[e[i].to]=dep[x]+1;
                dfs(e[i].to,x);
            }
    }
    
    int lca(int x,int y)
    {
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=dep[x]-dep[y],j=0;i;++j,i>>=1) 
            if(i&1) x=fa[x][j];
        if(x==y)return x;
        for(int i=MK;i>=0;i--)
            if(fa[x][i]!=fa[y][i])
                x=fa[x][i],y=fa[y][i];
        return fa[x][0];
    }
    
    int getfa(int x){return s[x]==x?x:s[x]=getfa(s[x]);}
    
    void Merge(int x,int y,int lim)
    {
        int f1=getfa(x),f2=getfa(y);
        now-=(size[f1]>=lim)+(size[f2]>=lim);
        s[f1]=f2,size[f2]+=size[f1];
        now+=(size[f2]>=lim);
    }
    
    int main()
    {
        freopen("summing19.in","r",stdin);
        freopen("summing.out","w",stdout);
        n=read();h=read();int xx;
        for(int i=1;i<n;i++)
            xx=read(),ins(xx,i);
        for(int i=0;i<=h;i++) a[i]=read();
        dfs(0,n);
        for(int i=1;i<=MK;i++)
            for(int j=0;j<n;j++)
                fa[j][i]=fa[fa[j][i-1]][i-1];
        for(int i=0;i<=h;++i) 
        {
            if(a[i]==0){ ans+=n;continue;}
            now=0;if(a[i]==1) now=v[i].size(),ans+=now;int pre=i;
            for(int j=0;j<v[i].size();++j) s[j]=j,size[j]=1;
            for(int j=1;j<v[i].size();++j)
                q.push((eve){dep[lca(v[i][j],v[i][j-1])],j,j-1});
            while(!q.empty())
            {
                eve th=q.top();q.pop();
                int d=th.dep;ans+=now*(pre-d-1);
                Merge(th.x,th.y,a[i]);
                while(!q.empty()&&q.top().dep==d)
                {
                    th=q.top();q.pop();
                    Merge(th.x,th.y,a[i]);
                }
                pre=d;ans+=now;
            }
            ans+=pre*now;
        }    
        cout<<ans<<endl;
        return 0;
    }

    B.三角形规划 (你的三角形已如风中残烛) 这个是上次校内训练赛的题..详见这里的C题

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #define eps 1e-10
    #define INF 2000000000
    #define T 9851
    #define S 0
    #define ld long double
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
     
    int head[T+5],d[T+5],q[T+5],cnt=1,n,tot,top,c[T+5];
    struct edge{
        int to,next;ld w;
    }e[1200000];
    bool b[55][55];
     
    void ins(int f,int t,ld w)
    {
        e[++cnt]=(edge){t,head[f],w};head[f]=cnt;
        e[++cnt]=(edge){f,head[t],0};head[t]=cnt;
    }
     
    ld dfs(int x,ld f)
    {
        if(x==T)return f;
        ld used=0;
        for(int&i=c[x];i;i=e[i].next)
        if(e[i].w>0&&d[e[i].to]==d[x]+1)
        {
            ld w=dfs(e[i].to,min(f-used,e[i].w));
            used+=w;e[i].w-=w;e[i^1].w+=w;
            if(fabs(f-used)<eps) return f; 
        }
        return used;
    }
     
    bool bfs()
    {
        memset(d,0,sizeof(d));int i,j;
        for(d[q[top=i=0]=S]=1;i<=top;i++)
            for(int j=c[q[i]]=head[q[i]];j;j=e[j].next)
                if(e[j].w>0&&!d[e[j].to])
                    d[q[++top]=e[j].to]=d[q[i]]+1;
        return d[T]>0;
    }
     
    void solve(int x)
    {
        if(!d[x])
        {
            d[x]=1;
            for(int&i=head[x];i;i=e[i].next)
                if(e[i].w>1e-6)
                    solve(e[i].to);
        }
    }
     
    int main()
    {
        freopen("triangle.in","r",stdin);
        freopen("triangle.out","w",stdout);
        tot=n=read();
        for(int i=1;i<=n;i++)
            ins(S,i,0);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                b[i][j]=read();
        for(int i=1;i<n;i++)
            for(int j=i+1;j<n;j++)
                for(int k=j+1;k<=n;k++)
                    if(b[i][j]&&b[i][k]&&b[j][k])
                    {
                        ++tot;ins(i,tot,INF);ins(j,tot,INF);
                        ins(k,tot,INF);ins(tot,T,1);
                    }
        ld l=0,r=393,mid,sum=0;
        int i,j;tot-=n;
        while(l+eps<r)
        {
            mid=(l+r)/2;sum=0;
            for(i=2,j=1;j<=n;j++,i+=2)
                e[i].w=mid,e[i^1].w=0;
            for(;i<=cnt;i+=2)
                e[i].w=1,e[i^1].w=0;
            while(bfs()) sum+=dfs(S,INF);
            if((ld)tot<=sum)r=mid;
            else l=mid;
        }
        top=0;memset(d,0,sizeof(d));
        solve(S);
        for(int i=1;i<=n;i++)if(!d[i])q[++top]=i;
        cout<<top<<endl;
        for(int i=1;i<=top;i++) printf("%d ",q[i]);
        return 0;
    }

    C.给定一个n个数的序列ai,求有多少对(i,j)满足ai*aj<=max(ai..aj)   n<=100000

    题解:枚举最大值是哪一个,往两边二分出它是最大值的范围,在两端较小的那一段枚举,另一端在主席树上查最值就行了。由于我们取较小的范围,所以查询次数最多nlogn次,复杂度$nlog^{2}n$

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #define ll long long
    #define INF 1000000000
    #define MN 100000
    #define MK 17
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    ll ans=0;
    int s[MN+5],n,p[MN+5],rt[MN+5],cnt=0;
    int f[MN+5][MK+3],f2[MN+5][MK+3];
    struct Tree{int l,r,x;}T[MN*35]; 
    
    int query(int x,int nx,int v)
    {
        if(x>nx) return 0;x=rt[x-1];nx=rt[nx];
        int l=1,r=INF,mid,ans=0;
        while(l<r)
        {
            mid=l+r>>1;
            if(v<=mid)
            {
                nx=T[nx].l;x=T[x].l;
                r=mid;
            }
            else
            {
                ans+=T[T[nx].l].x-T[T[x].l].x;
                nx=T[nx].r;x=T[x].r;l=mid+1;
            }
        }
        return ans+T[nx].x-T[x].x;
    }
    
    void ins(int x,int nx,int v)
    {
        int l=1,r=INF,mid;
        while(l<r)
        {
            mid=l+r>>1;
            if(v<=mid)
            {
                T[nx].r=T[x].r;T[nx].l=++cnt;
                x=T[x].l;nx=T[nx].l;r=mid;
            }
            else
            {
                T[nx].l=T[x].l;T[nx].r=++cnt;
                x=T[x].r;nx=T[nx].r;l=mid+1;
            }
            T[nx].x=T[x].x+1;
        }
    }
    
    int calc(int l,int r)
    {
        int len=p[r-l+1];
        return max(f[l][len],f2[r][len]);
    }
    
    int get(int l,int r,int ans,int x)
    {
        int mid,rr=r;
        while(l<=r)
        {
            mid=l+r>>1;
            if(calc(mid,r)<x) ans=mid,r=mid-1;
            else l=mid+1;
        }
        return ans;
    }
    
    int get2(int l,int r,int ans,int x)
    {
        int mid,lt=l;
        while(l<=r)
        {
            mid=l+r>>1;
            if(calc(lt,mid)<=x) ans=mid,l=mid+1;
            else r=mid-1;
        }
        return ans;
    }
    
    int main()
    {
        freopen("pair.in","r",stdin);
        freopen("pair.out","w",stdout);
        n=read();
        for(int i=0;i<=MK;i++)
            for(int j=(1<<i)+1;j<=min(n,1<<(i+1));j++) p[j]=i;
        for(int i=1;i<=n;i++)s[i]=f[i][0]=f2[i][0]=read();
        for(int i=1;i<=n;i++)ins(rt[i-1],rt[i]=++cnt,s[i]);
        for(int i=1,k=1;i<=MK;i++,k<<=1)
            for(int j=1;j<=n;j++)
                f[j][i]=max(f[j][i-1],f[min(j+k,n)][i-1]),
                f2[j][i]=max(f2[j][i-1],f2[max(1,j-k)][i-1]);
        for(int i=1;i<=n;i++)
        {
            int l=get(1,i-1,i,f[i][0]),r=get2(i+1,n,i,f[i][0]);
            if(i-l<r-i)
                for(int j=l;j<=i;j++) ans+=query(max(i,j+1),r,s[i]/s[j]);
            else
                for(int j=i;j<=r;j++) ans+=query(l,min(i,j-1),s[i]/s[j]);
        }
        cout<<ans<<endl;
        return 0;
    }

    D.需要你构造一个有n个点,每个点都恰好有m条单向的出边的图,使得点两两之间的最大距离最小。n<=1000,m<=5  当答案与最小直径相差不超过1的时候判对。

    题解:我们考虑一个倍增的思想。如果要表示1-7的数,我们会选择1 2 4。同理,我们对于每个点i,连接i和i*m+j  (j=0~m-1) ,这样在m^k>=n的时候就能保证任意一个点i在k步之内能走到任意一个点,直接算$m^{x}>=n$的x作为直径就好啦

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MN 1000
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    
    int n,m;
    
    int main()
    {
        freopen("diameter.in","r",stdin);
        freopen("diameter.out","w",stdout);
        n=read();m=read();
        int ans=0;
        for(int i=1;i<n;i*=m,ans++);
        cout<<ans<<endl;    
        for(int i=0;i<n;i++,puts(""))
            for(int j=0;j<m;j++)
                printf("%d ",(i*m+j)%n);
        return 0;
    }
  • 相关阅读:
    树上问题
    Windows Server 2012 安装dll到GAC
    一妹子开车发现车不动,男友听完她电话崩溃了
    各浏览器对 window.open() 的窗口特征 sFeatures 参数支持程度存在差异
    FullCalendar日历插件说明文档
    Request url 各种属性值
    Your Job Is Not to Write Code
    Nice way for strip_tags a like
    sqlserver 三种恢复模式
    ASP.NET路由
  • 原文地址:https://www.cnblogs.com/FallDream/p/liankao42.html
Copyright © 2020-2023  润新知