zoukankan      html  css  js  c++  java
  • 分治

    bzoj4367 假期(!!!

    题目大意:有n个点在一条直线上,每一时刻可以从i走向i+1或i-1,也可以停在i参观得到wi的收益,每个点只能贡献一次收益,求0时刻在st、m时刻最大收益。

    思路:f[i]、g[i]、fi[i]、gi[i]分别表示经过i时刻从st向右、向左、向左再回st、向右再回st的最大收益。有一点性质:设f[i]取最优值最远走到d[i],d[i]是单调不减的,所以可以分治(l,r,ll,rr)(表示从l~r时刻用ll~rr更新),mid=(l+r)/2,用ll~rr暴力更新f[mid],可以知道取前k大,k=mid-i+st,然后分治(l,mid-1,ll,d[mid])(mid+1,r,d[mid],rr);g和gi的更新d[i]是单调不增的;d的取值要尽量靠近mid();f和gi、fi和g中只能有一个能取到st这个点,最后答案就是max(i=0~m)(max(fi[i]+g[m-i],f[i]+gi[m-i]))。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 250005
    #define NN 100005
    #define M 2000005
    #define LL long long
    using namespace std;
    struct use{int l,r,sz;LL sm;}tr[M];
    int tt=0,rt[NN]={0},bz,st;
    LL f[N]={0},g[N]={0},fi[N]={0},gi[N]={0},wi[NN],bi[NN];
    void ins(int &x,int y,int l,int r,int z){
        tr[x=++tt]=tr[y];++tr[x].sz;
        tr[x].sm+=bi[z];if (l==r) return;
        int mid=(l+r)>>1;
        if (z<=mid) ins(tr[x].l,tr[y].l,l,mid,z);
        else ins(tr[x].r,tr[y].r,mid+1,r,z);}
    LL ask(int x,int y,int l,int r,int k){
        if (l==r) return min(k,tr[y].sz-tr[x].sz)*bi[l];
        int cc,mid=(l+r)>>1;
        cc=tr[tr[y].r].sz-tr[tr[x].r].sz;
        if (cc>=k) return ask(tr[x].r,tr[y].r,mid+1,r,k);
        else return tr[tr[y].r].sm-tr[tr[x].r].sm+ask(tr[x].l,tr[y].l,l,mid,k-cc);}
    void getf(int l,int r,int ll,int rr){
        int i,d,k,mid=(l+r)>>1;LL cc;
        for (i=ll;i<=rr;++i){
            k=mid-i+st;
            if (k<=0) continue;
            cc=ask(rt[st-1],rt[i],1,bz,k);
            if (cc>f[mid]){f[mid]=cc;d=i;}
        }if (l<mid) getf(l,mid-1,ll,d);
        if (r>mid) getf(mid+1,r,d,rr);}
    void getg(int l,int r,int ll,int rr){
        int i,k,d,mid=(l+r)>>1;LL cc;
        for (i=ll;i<=rr;++i){
            k=mid-(st-i);
            if (k<=0) continue;
            cc=ask(rt[i-1],rt[st-1],1,bz,k);
            if (cc>=g[mid]){g[mid]=cc;d=i;}
        }if (l<mid) getg(l,mid-1,d,rr);
        if (r>mid) getg(mid+1,r,ll,d);}
    void getfi(int l,int r,int ll,int rr){
        int i,k,d,mid=(l+r)>>1;LL cc;
        for (i=ll;i<=rr;++i){
            k=mid-2*(i-st);
            if (k<=0) continue;
            cc=ask(rt[st-1],rt[i],1,bz,k);
            if (cc>fi[mid]){fi[mid]=cc;d=i;}
        }if (l<mid) getfi(l,mid-1,ll,d);
        if (r>mid) getfi(mid+1,r,d,rr);}
    void getgi(int l,int r,int ll,int rr){
        int i,k,d,mid=(l+r)>>1;LL cc;
        for (i=ll;i<=rr;++i){
            k=mid-2*(st-i);
            if (k<=0) continue;
            cc=ask(rt[i-1],rt[st-1],1,bz,k);
            if (cc>=gi[mid]){gi[mid]=cc;d=i;}
        }if (l<mid) getgi(l,mid-1,d,rr);
        if (r>mid) getgi(mid+1,r,ll,d);}
    int main(){
        int n,m,i,ci;LL ans=0LL;
        scanf("%d%d%d",&n,&st,&m);++st;
        for (i=1;i<=n;++i){scanf("%I64d",&wi[i]);bi[i]=wi[i];}
        sort(bi+1,bi+n+1);
        bz=unique(bi+1,bi+n+1)-bi-1;
        for (i=1;i<=n;++i){
            ci=upper_bound(bi+1,bi+bz+1,wi[i])-bi-1;
            ins(rt[i],rt[i-1],1,bz,ci);
        }getf(1,m,st,n);getg(1,m,1,st);
        getfi(1,m,st,n);getgi(1,m,1,st);
        for (i=0;i<=m;++i) ans=max(ans,max(gi[i]+f[m-i],fi[i]+g[m-i]));
        printf("%I64d
    ",ans);
    }
    View Code

    bzoj4594 零件组装机(!!!

    题目大意:零件的生成方式如下:(1)可以新开一个点,编号0;(2)把n个点的和m个点的合并,n<=m,m个点重新编号为n~n+m-1,且>=n的点a向a%n连边。给出最后零件的图,问是否是个零件。

    思路:分治判断(l,r)能否合成零件,从r开始,找向前连边最早的>=l的点,%n情况下是连续递减的,0之后的那个点的%n的值就是n-1,就可以得出n(其中要求n<=(r-l+1)/2),n也有可能就是r处得到的,要特判一下,求最早的点是可以从前到后单调扫的。如果有自环就是NO。

    注意:多组数据的时候要读完。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    using namespace std;
    struct use{
        int u,v;
        bool operator<(const use&x)const{return (v==x.v ? u<x.u : v<x.v);}
    }ed[N];
    int fi[N],pre[N],n,m;
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    bool cg(int x,int l,int r){
        if (x<=0) return false;
        int i;
        for (i=l+x;i<=r;++i){
            if ((i-l)%x!=pre[i]-l) return false;
            if (ed[fi[i]].v==i&&ed[fi[i]].u<l+x&&fi[i]<=m) return false;
        }return true;
    }
    bool judge(int l,int r){
        int i,x=0;
        if (l==r) return true;
        for (i=l;i<=r;++i){
            if (pre[i]>=l) continue;
            for (;fi[i]<=m&&ed[fi[i]].v==i&&ed[fi[i]].u<l;++fi[i]);
            if (ed[fi[i]].v==i&&fi[i]<=m){pre[i]=ed[fi[i]].u;++fi[i];}
        }pre[l]=l;
        for (i=r;i>l;--i)
            if (pre[i]==l&&r-l+1>=(pre[i-1]-l+1)*2){
                x=pre[i-1]-l+1;break;
            }
        if (!cg(x,l,r)&&(r-pre[r])*2>=r-l+1&&pre[r]>=l) x=r-pre[r];
        if (cg(x,l,r)&&judge(l,l+x-1)&&judge(l+x,r)) return true;
        return false;
    }
    int main(){
        int t,i,j,u,v;bool f;t=in();
        while(t--){
            n=in();m=in();f=false;
            for (i=1;i<=m;++i){
                u=in();v=in();
                if (u>v) swap(u,v);
                ed[i]=(use){u,v};
                if (u==v) f=true;
            }if (f){printf("NO
    ");continue;}
            sort(ed+1,ed+m+1);
            for (i=0;i<n;++i) pre[i]=-1;
            for (j=1,i=0;i<n;++i){
                fi[i]=j;
                for (;ed[j].v==i&&j<=m;++j);
            }if (judge(0,n-1)) printf("YES
    ");
            else printf("NO
    ");
        }
    }
    View Code

    bzoj2458 最小三角形

    题目大意:给出n个点,求周长最小的三角形,三点可共线。

    思路:类似平面最近点对,解决完l~mid和mid+1~r之后,找到ai[mid].x左右mn范围内的点按y排序,加一些最优化剪枝,暴力n^3统计答案。

    注意:如果是要先归并再统计答案的话,会把mid位置的点改变,不能正确统计。这题中的y可以只对每次用的那些点排序,原数组不需要更改。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define N 200005
    #define LD double
    #define eps 1e-9
    #define inf 1e20
    using namespace std;
    int cmp(LD x,LD y){
        if (x-y>eps) return 1;
        if (y-x>eps) return -1;
        return 0;}
    LD ab(LD x){return cmp(x,0.)<0 ? -x : x;}
    struct use{
        LD x,y;
        bool operator<(const use&a)const{return cmp(x,a.x)<0;}
    }ai[N],bi[N];
    LD mn;
    LD cmpy(use a,use b){return cmp(a.y,b.y)<0;}
    LD sqr(LD x){return x*x;}
    LD dis(use x,use y){return sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));}
    LD getc(use a,use b,use c){return dis(a,b)+dis(b,c)+dis(a,c);}
    void solve(int l,int r){
        if (l==r) return;
        int i,j,k,bz=0,mid=(l+r)>>1;LD v;
        solve(l,mid);solve(mid+1,r);
        for (i=l;i<=r;++i)
            if (cmp(ab(ai[mid].x-ai[i].x),mn)<=0) bi[++bz]=ai[i];
        sort(bi+1,bi+bz+1,cmpy);
        for (i=1;i<=bz;++i)
            for (j=i+1;j<=bz;++j){
                if (cmp(bi[j].y-bi[i].y,mn)>=0) break;
                for (k=j+1;k<=bz;++k){
                    if (cmp(bi[k].y-bi[i].y,mn/2.)>=0) break;
                    mn=min(mn,getc(bi[i],bi[j],bi[k]));
                }
            }
    }
    int main(){
        int n,i;scanf("%d",&n);
        for (i=1;i<=n;++i) scanf("%lf%lf",&ai[i].x,&ai[i].y);
        sort(ai+1,ai+n+1);
        mn=inf;solve(1,n);
        printf("%.6f
    ",mn);
    }
    View Code

    点分治

    poj1741树上的点对

    题目大意:给定一棵树,求树上距离不超过k的点对个数。

    思路:点分治模板题。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #define maxnode 10005
    #define inf 2100000000LL
    using namespace std;
    int point[maxnode]={0},next[maxnode*2]={0},en[maxnode*2]={0},va[maxnode*2]={0},
        siz[maxnode]={0},tot=0,maxn,root,ans=0,k,n;
    bool visit[maxnode]={0};
    vector<int> dis;
    void add(int u,int v,int w)
    {
        ++tot;next[tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=w;
        ++tot;next[tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=w;
    }
    void getroot(int u,int fa,int nn)
    {
        int i,maxsiz=0,j;
        siz[u]=1;
        for (i=point[u];i;i=next[i])
        {
            if (!visit[j=en[i]]&&j!=fa)
            {
                getroot(j,u,nn);siz[u]+=siz[j];
                maxsiz=max(maxsiz,siz[j]);
            }
        }
        maxsiz=max(maxsiz,nn-siz[u]);
        if (maxsiz<maxn)
        {
            maxn=maxsiz;root=u;
        }
    }
    void getdep(int u,int dep,int fa)
    {
        int i;
        dis.push_back(dep);siz[u]=1;
        for (i=point[u];i;i=next[i])
            if (en[i]!=fa&&!visit[en[i]]) 
            {
              getdep(en[i],dep+va[i],u);siz[u]+=siz[en[i]];
            }
    }
    int cal(int u,int dep)
    {
        int sum=0,i,j;    
        dis.clear();getdep(u,dep,0);
        sort(dis.begin(),dis.end());
        for (i=0,j=dis.size()-1;i<j;)
        {
            if (dis[i]+dis[j]<=k) 
            {
              sum+=j-i;++i;
            }
            else --j;
        }
        return sum;
    }
    void work(int u)
    {
        int i,j;
        ans+=cal(u,0);visit[u]=true;
        for (i=point[u];i;i=next[i])
        {
            if (!visit[en[i]])
            {
                ans-=cal(en[i],va[i]);
                maxn=siz[en[i]];
                getroot(en[i],root=0,siz[en[i]]);
                work(root);
            }
        }
    }
    int main()
    {
        freopen("poj1741_tree.in","r",stdin);
        freopen("poj1741_tree.out","w",stdout);
        
        int i,j,u,v,w;
        while(scanf("%d%d",&n,&k)==2)
        {
            if (n==0&&k==0) break;
            tot=0;memset(visit,false,sizeof(visit));
            memset(point,0,sizeof(point));
            for (i=1;i<n;++i)
            {
                scanf("%d%d%d",&u,&v,&w);
                add(u,v,w);
            }
            maxn=n;ans=0;
            getroot(1,root=0,n);
            work(root);
            printf("%d
    ",ans);
        }
    }
    View Code

    hdu4670Cube number on a tree 
    题目大意:给定一棵树,每个点都有一个权值,可以分为一些给定质数的积(质数个数不超过30),求出一些路径的条数,使路径都满足路上点权乘积是个立方数。 
    思路:我们对于每一个点都分解质因数,保存下每种的个数,满足条件的路径就是那些点的相应因数个数相加后%3=0的路径了。 
    点分,对每一个点统计路径个数,减去重复的就可以了。 
    对于统计答案的部分,我们dfs求出到点到重心路径上的和,把这个30位的数组转成一个三进制数,放到map里。穷举每一个map里面的数,找到互补的数,贡献给答案就可以了。这里注意如果这个数和互补数一样的话,ans的计算方式略有不同。 
    对于重复的部分,我们减去的时候要时刻注意变量的含义。 
    这道题目的思维和代码量都中等,但是结合起来莫名的很难写(蒟蒻的吐槽),要认真思考好每一个部分的实现。 

    #pragma comment(linker,"/STACK:1024000000,1024000000")
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #define maxnode 50005
    #define inf 2100000000LL
    #define sta 30
    using namespace std;
    struct use{
        int num[35];
        void init()
        {
            int i;
            for (i=0;i<35;++i) num[i]=0;
        }
    }ai[maxnode]={0};
    int prime[35]={0},point[maxnode]={0},nex[maxnode*2]={0},en[maxnode*2]={0},tot=0,
        root,son[maxnode]={0},siz[maxnode]={0};
    long long ans=0,mi[35]={0};
    bool visit[maxnode]={false};
    map <long long,long long> cnt;
    void add(int u,int v)
    {
        ++tot;nex[tot]=point[u];point[u]=tot;en[tot]=v;
        ++tot;nex[tot]=point[v];point[v]=tot;en[tot]=u;
    }
    void fp(int j,long long x)
    {
        int i;
        ai[j].init();
        for (i=1;i<=prime[0];++i)
        {
            while(x!=0&&x%prime[i]==0)
            {
                x/=prime[i];++ai[j].num[i];
                if (ai[j].num[i]>=3) ai[j].num[i]%=3;
            }
        }
    }
    long long threezten(use x)
    {
        int i,j;
        long long sum=0;
        for (i=1;i<=sta;++i)
            sum+=mi[i-1]*(x.num[i]%3);
        return sum;
    }
    use tenzthree(long long x)
    {
        int i,j;
        use y;
        y.init();i=1;
        while(x)
        {
            y.num[i]=x%3;x/=3;++i;
        }
        return y;
    }
    use jia(use x,use y)
    {
        int i;
        for (i=1;i<=sta;++i) x.num[i]=(x.num[i]+y.num[i])%3;
        return x;
    }
    use jian(use x,use anc)
    {
        int i;
        use y;
        for (i=1;i<=sta;++i) y.num[i]=(3-x.num[i]+anc.num[i])%3;
        return y;
    }
    bool equal(use x,use y)
    {
        int i;
        for (i=1;i<=sta;++i) if (x.num[i]!=y.num[i]) return false;
        return true;
    }
    void getroot(int u,int fa,int up)
    {
        int i,j;
        siz[u]=1;son[u]=0;
        for (i=point[u];i;i=nex[i])
        {
            if ((j=en[i])!=fa&&!visit[j])
            {
                getroot(j,u,up);siz[u]+=siz[j];
                son[u]=max(son[u],siz[j]);
            }
        }
        son[u]=max(son[u],up-siz[u]);
        if (son[u]<son[root]) root=u;
    }
    void getdis(int u,int fa,use dis)
    {
        int i,j;
        long long kk;
        if (!cnt.count(kk=threezten(dis))) cnt[kk]=0;
        ++cnt[kk];siz[u]=1;
        for (i=point[u];i;i=nex[i])
        {
            if ((j=en[i])!=fa&&!visit[j])
            {
                getdis(j,u,jia(dis,ai[j]));siz[u]+=siz[j];
            }
        }
    }
    long long calc(int u,use cc,use anc)
    {
        int i,j;
        long long sum=0,kk;
        use x,y;
        cnt.clear();
        getdis(u,0,jia(cc,ai[u]));
        map<long long,long long>::iterator it;
        for (it=cnt.begin();it!=cnt.end();++it)
        {
            if (it->second!=0)
            {
                x=tenzthree(it->first);y=jian(x,anc);
                if (cnt.count(kk=threezten(y)))
                {
                    if (equal(x,y)) sum+=(cnt[kk]+cnt[kk]*(cnt[kk]-1)/2);
                    else sum+=((it->second)*(cnt[kk]));
                    cnt[kk]=0;it->second=0;
                }
            }
        }
        return sum;
    }
    void work(int u)
    {
        int i,j;
        use kk;
        kk.init();visit[u]=true;
        ans+=calc(u,kk,ai[u]);
        for (i=point[u];i;i=nex[i])
        {
            if (!visit[j=en[i]])
            {
                ans-=calc(j,ai[u],ai[u]);
                root=0;son[root]=inf;
                getroot(j,0,siz[j]);
                work(root);
            }
        }
    }
    int main()
    {
        int n,i,j,k,u,v;
        long long aa;
        mi[0]=1;
        for (i=1;i<=sta;++i) mi[i]=mi[i-1]*3;
        while(scanf("%d%d",&n,&prime[0])==2)
        {
          ans=tot=0;
          memset(point,0,sizeof(point));
          memset(nex,0,sizeof(nex));
          memset(visit,false,sizeof(visit));
          for (i=1;i<=prime[0];++i) scanf("%d",&prime[i]);
          for (i=1;i<=n;++i)
          {
            scanf("%I64d",&aa);fp(i,aa);
          }
          for (i=1;i<n;++i)
           {
            scanf("%d%d",&u,&v);add(u,v);
          }
          root=0;son[root]=inf;getroot(1,0,n);
          work(root);
          printf("%I64d
    ",ans);
        }
    }
    View Code

    bzoj2152聪聪可可

    题目大意:给定一棵树,求边权和是三的倍数的路径数(同一个点和正反路径都算不同的路径),以分数的形式表示出来(分母是总的路径数n^2)。

    思路:点分,用0、1、2为下标保存路径个数,然后相应的乘起来就可以了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxedge 20005
    #define maxnode 20005
    using namespace std;
    int point[maxedge*2]={0},next[maxedge*2]={0},en[maxedge*2]={0},siz[maxnode]={0},va[maxedge*2]={0},
        ans=0,root,maxn,tot=0,zhan[3]={0},dis[maxnode]={0};
    bool visit[maxnode]={false};
    int gcd(int x,int y)
    {
        if (!y) return x;
        else return gcd(y,x%y);
    }
    void add(int x,int y,int w)
    {
        ++tot;next[tot]=point[x];point[x]=tot;en[tot]=y;va[tot]=w;
        ++tot;next[tot]=point[y];point[y]=tot;en[tot]=x;va[tot]=w;
    }
    void getroot(int u,int fa,int al)
    {
        int i,j,maxsiz=0;  siz[u]=1;
        for (i=point[u];i;i=next[i])
        {
            if ((j=en[i])!=fa&&!visit[j])
            {
                getroot(j,u,al);siz[u]+=siz[j];
                maxsiz=max(maxsiz,siz[j]);
            }
        }
        maxsiz=max(maxsiz,al-siz[u]);
        if (maxsiz<maxn){maxn=maxsiz;root=u;}
    }
    void getdep(int u,int fa,int dep)
    {
        int i,j; dis[u]=dep;siz[u]=1;++zhan[dep];
        for (i=point[u];i;i=next[i])
        {
            if (!visit[j=en[i]]&&j!=fa)
            {
                getdep(j,u,(dep+va[i])%3);siz[u]+=siz[j];
            }
        }
    }
    int calc(int u,int al)
    {
        int i,j,sum=0;
        memset(zhan,0,sizeof(zhan));
        getdep(u,0,al);
        sum+=zhan[0]*zhan[0]+2*zhan[1]*zhan[2];
        return sum;
    }
    void work(int u)
    {
        int i,j;
        ans+=calc(u,0);visit[u]=true;
        for (i=point[u];i;i=next[i])
        {
            if (!visit[j=en[i]])
            {
                ans-=calc(j,va[i]%3);maxn=siz[j];
                getroot(j,root=0,siz[j]);
                work(root);
            }
        }
    }
    int main()
    {
        int i,j,n,x,y,w,mu,k;
        scanf("%d",&n);mu=n*n;
        for (i=1;i<n;++i)
        {
            scanf("%d%d%d",&x,&y,&w);
            add(x,y,w);
        }
        maxn=n;getroot(1,root=0,n);work(root);
        k=gcd(mu,ans);mu/=k;ans/=k;
        printf("%d/%d
    ",ans,mu);
    }
    View Code

    bzoj4016最短路径树问题

    题目大意:给定一个无向图,求最短路径树(从1开始且每个点到根的路径字典序最小),然后求最短路径树上的经过k个点的最长路径以及经过k个点的长度为最长路径长度的路径个数。

    思路:用点分,在更新最长路径的时候更新一下方案数,注意不能简单的+1-1,而是一些累加。

             一开始理解错了题意(理解成了路径长度为最长路径长度,但经过点的个数不一定是k的路径个数),写了两个点分wa+tle。。。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<vector>
    #define maxnode 30005
    #define maxedge 60005
    #define len 100000
    using namespace std;
    struct use{
        int dian,dx;
    };
    int poi[maxnode]={0},nex[maxedge*2]={0},enn[maxedge*2]={0},vaa[maxedge*2]={0},to=0,k,dis[maxnode]={0},que[len+5]={0},ans=0,root={0},
        point[maxnode]={0},next[maxedge*2]={0},en[maxedge*2]={0},va[maxedge*2]={0},tot=0,siz[maxnode]={0},maxn,n,kk=0,zhan[maxnode][2]={0};
    bool visit[maxnode]={false},flag[maxedge*2]={false};
    void add1(int x,int y,int w)
    {
        ++to;nex[to]=poi[x];poi[x]=to;enn[to]=y;vaa[to]=w;
        ++to;nex[to]=poi[y];poi[y]=to;enn[to]=x;vaa[to]=w;
    }
    void add(int x,int y,int w)
    {
        ++tot;next[tot]=point[x];point[x]=tot;en[tot]=y;va[tot]=w;
        ++tot;next[tot]=point[y];point[y]=tot;en[tot]=x;va[tot]=w;
    }
    void spfa()
    {
        int x,y,i,j,tail,head;
        memset(dis,127,sizeof(dis));dis[1]=0;visit[1]=true;
        head=tail=0;que[++tail]=1;
        while(head!=tail)
        {
            head=head%len+1;
            x=que[head];visit[x]=false;
            for (y=poi[x];y;y=nex[y])
            {
                if (dis[enn[y]]>dis[x]+vaa[y])
                {
                    dis[enn[y]]=dis[x]+vaa[y];
                    if (!visit[enn[y]])
                    {
                        visit[enn[y]]=true;tail=tail%len+1;
                        que[tail]=enn[y];
                    }
                }
            }
        }
        for (i=1;i<=n;++i)
            for (j=poi[i];j;j=nex[j])
                if (dis[enn[j]]==dis[i]+vaa[j]) flag[j]=true;
    }
    int my_comp(const use x,const use y)
    {
        if (x.dian<y.dian) return 1;
        else return 0;
    }
    void build(int u,int fa,int dd)
    {
        int i,j;
        vector<use> cc;
        use xx;
        cc.clear();visit[u]=true;
        if (fa) add(fa,u,dd);
        for (i=poi[u];i;i=nex[i])
          if (flag[i]) {xx.dian=enn[i];xx.dx=vaa[i];cc.push_back(xx);}
        sort(cc.begin(),cc.end(),my_comp);
        for (i=0;i<cc.size();++i)
            if (!visit[cc[i].dian]) build(cc[i].dian,u,cc[i].dx);
    }
    void getroot(int u,int fa,int al)
    {
        int i,j,maxsiz=0; siz[u]=1;
        for (i=point[u];i;i=next[i])
        {
            if (!visit[j=en[i]]&&j!=fa)
            {
                getroot(j,u,al);siz[u]+=siz[j];
                maxsiz=max(maxsiz,siz[j]);
            }
        }
        maxsiz=max(maxsiz,al-siz[u]);
        if (maxsiz<maxn){root=u;maxn=maxsiz;}
    }
    void getdep(int u,int fa,int dep,int dd)
    {
        int i,j;
        dis[u]=dep;
        if (k-1-dd>=0)
        {
          if ((j=dis[u]+zhan[k-1-dd][0])>=kk)
          {
            if (j==kk) ans+=zhan[k-1-dd][1];
            else {ans=zhan[k-1-dd][1];kk=j;}
          }
        }
        for (i=point[u];i;i=next[i])
            if (!visit[j=en[i]]&&j!=fa)  getdep(j,u,dep+va[i],dd+1);
    }
    void ins(int u,int fa,int dep,int dd)
    {
        int i,j;
        siz[u]=1;dis[u]=dep;
        if (zhan[dd][0]<=dis[u])
        {
            if (zhan[dd][0]<dis[u]) {zhan[dd][0]=dis[u];zhan[dd][1]=1;}
            else ++zhan[dd][1];
        }
        for (i=point[u];i;i=next[i])
            if (!visit[j=en[i]]&&j!=fa)
            {
                ins(j,u,dep+va[i],dd+1);siz[u]+=siz[j];
            }
    }
    void workk(int u)
    {
         int i,j,x,y;
         visit[u]=true;memset(zhan,0,sizeof(zhan));zhan[0][0]=0;zhan[0][1]=1;
         for (i=point[u];i;i=next[i])
         {
             if(!visit[j=en[i]]){getdep(j,0,va[i],1);ins(j,0,va[i],1);}
         }
         for (i=point[u];i;i=next[i])
         {
            if (!visit[j=en[i]])
            {
                maxn=siz[j];getroot(j,root=0,siz[j]);workk(root);
            }
         }
    }
    void getk()
    {
        int i,j,x,y;
        memset(visit,false,sizeof(visit));
        maxn=n;getroot(1,root=0,n);workk(root);
    }
    int main()
    {
        int m,i,j,t,x,y,w;
        scanf("%d%d%d",&n,&m,&k);
        for (i=1;i<=m;++i)
        {
            scanf("%d%d%d",&x,&y,&w);
            add1(x,y,w);
        }
        spfa();memset(visit,false,sizeof(visit));
        build(1,0,0);getk();
        printf("%d %d
    ",kk,ans);
    }
    View Code

    bzoj1758 重建计划

    题目大意:求树上长度在l~u的链的sigma va[i]/|s|的最大值。

    思路:分数规划+分治+单调队列。分数规划之后就是求sigma va[i]-mid的最长链是否>=0,点分治之后,用单调队列维护深度递减的最大值,然后更新最大值。

    有一些优化:1)点分治里分数规划会使下界变优;

          2)二分的时候,mid=(l*9+r)/10,会快很多。

          3)点分每个u算答案的时候,要用儿子的siz/dep从小到大排序之后算,否则可能会被扫把型的数据卡掉。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 200005
    #define LD double
    #define eps 1e-9
    #define ege 1e-4
    #define inf 1000000000000LL
    using namespace std;
    struct use{int x;LD y;}pri[N];
    struct uss{
        int x,y;LD vv;
        bool operator<(const uss&a)const{return y<a.y;}
    }qz[N];
    int point[N]={0},next[N],en[N],tot=0,ll,uu,siz[N],cur=0,mx,rt,root,mxdep,zh[N],dep[N],
        fa[N],pl,pr,qr,qt;
    LD va[N],ans,que[2][N],sum[N],as,hi;
    bool vi[N]={false};
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    int cmp(LD x,LD y){
        if (y-x>eps) return -1;
        return 0;}
    void add(int u,int v,LD vv){
        next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=vv;
        next[++tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=vv;}
    void getrt(int u,int ff,int nn){
        int i,v,mz=0;siz[u]=1;
        for (i=point[u];i;i=next[i]){
            if ((v=en[i])==ff||vi[v]) continue;
            getrt(v,u,nn);siz[u]+=siz[v];
            mz=max(mz,siz[v]);
        }mz=max(mz,nn-siz[u]);
        if (mz<mx){mx=mz;rt=u;}
    }
    void bfs(int u,int ff,LD sm,LD xx){
        int head,tail,i,v;
        qr=min(mxdep,uu);cur^=1;pl=1;pr=0;
        head=tail=0;fa[u]=ff;
        zh[++tail]=u;dep[u]=1;sum[u]=sm;
        for (i=0;i<=qr;++i) que[cur][i]=que[cur^1][i];
        while(head!=tail){
            u=zh[++head];mxdep=max(mxdep,dep[u]);
            if (dep[u]>uu) return;
            que[cur][dep[u]]=max(que[cur][dep[u]],sum[u]);
            while(qr+dep[u]>uu&&qr) --qr;
            while(qr+dep[u]>=ll&&qr){
                while(pl<=pr&&cmp(que[cur^1][qr],pri[pr].y)>=0) --pr;
                pri[++pr]=(use){qr,que[cur^1][qr]};--qr;
            }while(pl<=pr&&pri[pl].x+dep[u]>uu) ++pl;
            if (pl<=pr){
                ans=max(ans,sum[u]+pri[pl].y);
                if (cmp(ans,0.)>=0) return;
            }for (i=point[u];i;i=next[i]){
                if (vi[v=en[i]]||v==fa[u]) continue;
                fa[v]=u;dep[v]=dep[u]+1;
                sum[v]=sum[u]+va[i]-xx;
                zh[++tail]=v;
            }
        }
    }
    LD calc(int u,LD xx){
        int i,v;mxdep=0;ans=-(LD)inf;
        for (cur=0,i=min(siz[u],uu);i>=0;--i) que[0][i]=que[1][i]=-(LD)inf;
        for (qt=0,i=point[u];i;i=next[i]){
            if (vi[v=en[i]]) continue;
            qz[++qt]=(uss){v,siz[v],va[i]};
        }sort(qz+1,qz+qt+1);
        for (i=1;i<=qt;++i){
            bfs(qz[i].x,u,qz[i].vv-xx,xx);
            if (cmp(ans,0.)>=0) return ans;
        }mxdep=min(mxdep,uu);
        for (i=ll;i<=mxdep;++i) ans=max(ans,que[cur][i]);
        return ans;}
    void work(int u){
        int i,v;LD l,r,mid;
        l=as;r=hi;
        while(r-l>ege){
            mid=(l*9.+r)/10.;
            if (cmp(calc(u,mid),0.)>=0) l=mid;
            else r=mid;
        }as=l;vi[u]=true;
        for (i=point[u];i;i=next[i]){
            if (vi[v=en[i]]) continue;
            getrt(v,u,mx=siz[v]);work(rt);
        }
    }
    int main(){
        int n,i,u,v;LD vv;
        n=in();ll=in();uu=in();
        for (hi=0.,i=1;i<n;++i){
            u=in();v=in();vv=(LD)in();
            add(u,v,vv);hi=max(hi,vv);
        }tot=0;getrt(1,0,mx=n);
        tot=2*n-2;work(rt);
        printf("%.3f
    ",as);
    }
    View Code

    cdq分治

    bzoj1492 货币兑换

    题目大意:一开始有s元钱,每天可以卖出任意相等比例的a、b券,也可以买入一定比例的a、b券,已知每天a、b券的价格和比例,求n天后最多能有多少钱。

    思路:每次买卖都是全买全卖。设fi[i]表示i这一天全换成a、b券后a的数量。O(n^2)的做法是money=fi[j]*ai[i]+fi[j]/rate[j]*bi[i],fi[i]=max{money*rate[i]/(ai[i]*rate[i]+bi[i])},这个式子可以斜率优化,设l<j,且j更优,则有fi[j]ai[i]+fi[j]bi[i]/rate[j]-fi[l]ai[i]-fi[l]bi[i]/rate[l]>0=ai[i](fi[j]-fi[l])+bi[i](fi[j]/rate[j]-fi[l]/rate[l])>0,因为只有money跟j有关,所以变形一下fi[j]/rate[j]=-fi[j]*ai[i]/bi[i]+money/bi[i],相当于求一个斜率为-ai[i]/bi[i]的过(fi[j],fi[j]/rate[j])直线的最大截距。但是x不是单调的,所以可以用splay维护凸包。更简单的做法是用cdq分治,在用l~mid更新mid+1~r的时候,做一个l~mid上凸包,同时mid+1~r的-ai[i]/bi[i]按降序排序,然后单调扫一遍,更新答案就可以了。

    注意:这里fi是a的数量,而不是最终的钱数,需要计算一下。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    #define LD double
    #define eps 1e-9
    using namespace std;
    int cmp(LD x,LD y){
        if (x-y>eps) return 1;
        if (y-x>eps) return -1;
        return 0;}
    struct point{LD x,y;int po;}gr[N];
    point operator-(point a,point b){return (point){a.x-b.x,a.y-b.y,0};}
    LD cross(point a,point b){return a.x*b.y-a.y*b.x;}
    LD ai[N],bi[N],rt[N],s,fi[N]={0.},gi[N],mo[N],ans;
    int n,cnt[20],li[N],ri[N],gnt;
    int cmpl(int a,int b){
        return (cmp(fi[a],fi[b])==0 ? cmp(gi[a],gi[b])<0 : cmp(fi[a],fi[b])<0);}
    int cmpr(int a,int b){
        return (cmp(ai[a]*bi[b],ai[b]*bi[a])<0);}
    void graham(int l,int r){
        int i;point xx;gnt=0;
        sort(li+l,li+r+1,cmpl);
        for (i=r;i>=l;--i){
            xx=(point){fi[li[i]],gi[li[i]],li[i]};
            while(gnt>1 && cmp(cross(gr[gnt]-gr[gnt-1],xx-gr[gnt-1]),0.)<=0) --gnt;
            gr[++gnt]=xx;
        }
    }
    LD getk(point a,point b){return (a.y-b.y)/(a.x-b.x);}
    void get(int i,int j){
        mo[i]=max(mo[i],fi[j]*ai[i]+fi[j]*bi[i]/rt[j]);
        ans=max(ans,mo[i]);}
    void calc(int l,int r){
        int i,j;LD k;
        for (i=l;i<=r;++i){
            j=ri[i];k=-ai[j]/bi[j];
            while(gnt>1&&cmp(getk(gr[gnt],gr[gnt-1]),k)>=0) --gnt;
            get(j,gr[gnt].po);
        }for (i=l;i<=r;++i){
            mo[i]=max(mo[i],mo[i-1]);
            fi[i]=max(fi[i],mo[i]*rt[i]/(ai[i]*rt[i]+bi[i]));
            gi[i]=fi[i]/rt[i];
        }
    }
    void solve(int l,int r){
        if (l==r){
            gi[l]=fi[l]/rt[l];return;
        }int mid=l+r>>1;int i;
        solve(l,mid);
        for (i=l;i<=mid;++i) li[i]=i;
        for (i=mid+1;i<=r;++i) ri[i]=i;
        sort(ri+mid+1,ri+r+1,cmpr);
        graham(l,mid);calc(mid+1,r);
        solve(mid+1,r);
    }
    int main(){
        int i;scanf("%d%lf",&n,&s);ans=s;
        for (i=1;i<=n;++i) scanf("%lf%lf%lf",&ai[i],&bi[i],&rt[i]);
        fi[1]=s*rt[1]/(ai[1]*rt[1]+bi[1]);
        gi[1]=fi[1]/rt[1];mo[0]=mo[1]=s;
        solve(1,n);printf("%.3f
    ",ans);
    }
    View Code

    bzoj1176 Mokia

    题目大意:在二维矩阵中支持操作:1)单点加;2)矩形中权值求和。

    思路:solve(l,r)处理的是l~mid中1)操作对mid+1~r中2)操作的影响,因为每个区间之间的更新互不影响,所以可以直接归并排序。处理影响的时候,经典离线线段树做法。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 2000005
    #define M 200005
    #define LL long long
    using namespace std;
    struct use{int x,y,k,po;}ask[M];
    LL cc[N]={0},ans[M]={0};
    int w,n=0,cnt=0,flag[N]={0},ai[M],ci[M];
    int lowbit(int x){return x&(-x);}
    int cmp(int x,int y){return ask[x].x<ask[y].x;}
    void ins(int x,LL y){
        for (;x<=w;x+=lowbit(x)){
            if (flag[x]==cnt) cc[x]+=y;
            else{cc[x]=y;flag[x]=cnt;}
        }
    }
    LL query(int x){
        LL sum=0LL;
        for (;x;x-=lowbit(x))
            if (flag[x]==cnt) sum+=cc[x];
        return sum;
    }
    void work(int l,int r){
        int i,j,mid=(l+r)>>1;++cnt;
        for (i=l,j=mid+1;j<=r;++j){
            while(i<=mid&&ask[ai[i]].x<=ask[ai[j]].x){
                if (ask[ai[i]].k>0) ins(ask[ai[i]].y,(LL)ask[ai[i]].k);
                ++i;
            }ans[ask[ai[j]].po]+=(ask[ai[j]].k==0 ? 1LL : -1LL)*query(ask[ai[j]].y);
        }
    }
    void merge(int l,int r){
        int i,j,k,mid;mid=(l+r)>>1;
        i=l;j=mid+1;k=l-1;
        while(i<=mid&&j<=r){
            if (ask[ai[i]].x<ask[ai[j]].x) ci[++k]=ai[i++];
            else ci[++k]=ai[j++];
        }for (;i<=mid;++i) ci[++k]=ai[i];
        for (;j<=r;++j) ci[++k]=ai[j];
        for (i=l;i<=r;++i) ai[i]=ci[i];
    }
    void solve(int l,int r){
        if (l==r) return;
        int mid=(l+r)>>1;
        solve(l,mid);solve(mid+1,r);
        work(l,r);merge(l,r);
    }
    int main(){
        int i,k,x,y,a,x1,y1,m=0;
        while(scanf("%d",&k)==1){
            if (k==3) break;
            if (k==0){scanf("%d",&w);continue;}
            scanf("%d%d",&x,&y);
            if (k==1){
                scanf("%d",&a);
                ask[++n]=(use){x,y,a,0};
            }if (k==2){
                scanf("%d%d",&x1,&y1);++m;
                ask[++n]=(use){x1,y1,0,m};
                if (x>1&&y>1) ask[++n]=(use){x-1,y-1,0,m};
                if (x>1) ask[++n]=(use){x-1,y1,-1,m};
                if (y>1) ask[++n]=(use){x1,y-1,-1,m};
            }
        }for (i=1;i<=n;++i) ai[i]=i;
        solve(1,n);
        for (i=1;i<=m;++i) printf("%I64d
    ",ans[i]);
    }
    View Code

    bzoj3672 购票

    题目大意:给定一棵树,1为根,每个点的代价是f[u]=f[a]+p[u]*(dep[u]-dep[a])+q[u](dep[u]-dep[a]<=lim[u]),求每个点的最小代价。

    思路:点分治+cdq分治。这个式子可以斜率优化。点分治之后,考虑用这一次点分的根rt~u的点更新非u父亲所在子树,先分治u父亲所在子树,这个时候rt~u的点都已经更新到最优答案,把这些点找到,同时找到其他子树中的儿子,按能到的深度递减排序,边维护下凸包,边更新这些儿子的代价,然后分治这些子树。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #define N 200005
    #define LL long long
    #define LD double
    #define eps 1e-9
    using namespace std;
    struct point{LD x,y;}gi[N];
    point operator-(point a,point b){return (point){a.x-b.x,a.y-b.y};}
    LD cross(point a,point b){return a.x*b.y-a.y*b.x;}
    int po[N]={0},next[N],en[N],tot=0,gr[N],fa[N]={0},siz[N],n,mx,rt,zh[N],t;
    LL va[N],dep[N]={0LL},pi[N],qi[N],lim[N],lm[N],fi[N];
    bool vi[N]={false},fb[N]={false};
    int cmp(LD x,LD y){
        if (x-y>eps) return 1;
        if (y-x>eps) return -1;
        return 0;}
    int mcm(int x,int y){return lm[x]<lm[y];}
    void add(int u,int v,LL vv){next[++tot]=po[u];po[u]=tot;en[tot]=v;va[tot]=vv;}
    void pre(int u,int ff){
        int i,v;
        for (i=po[u];i;i=next[i]){
            if ((v=en[i])==ff) continue;
            dep[v]=dep[u]+va[i];pre(v,u);
        }
    }
    void getrt(int u,int ff,int nn){
        int i,v,mz=0;siz[u]=1;
        for (i=po[u];i;i=next[i]){
            if (vi[v=en[i]]||v==ff||fb[v]) continue;
            getrt(v,u,nn);siz[u]+=siz[v];
            if (siz[v]>mz) mz=siz[v];
        }mz=max(mz,nn-siz[u]);
        if (mz<mx){mx=mz;rt=u;}
    }
    void dfs(int u,int ff,int anc){
        int i,v;lm[u]=lim[u]-(dep[u]-dep[anc]);
        if (lm[u]>=0) zh[++zh[0]]=u;
        if (fb[u]) return;
        for (i=po[u];i;i=next[i]){
            if (vi[v=en[i]]||v==ff) continue;
            dfs(v,u,anc);
        }
    }
    LD getf(int x){return (gi[gr[x]].y-gi[gr[x-1]].y)/(gi[gr[x]].x-gi[gr[x-1]].x);}
    void updata(int u,int v){
        fi[u]=min(fi[u],fi[v]+pi[u]*(dep[u]-dep[v])+qi[u]);
        gi[u]=(point){(LD)dep[u],(LD)fi[u]};}
    void ask(int u){
        int l,r,mid,ci;
        if (t<1) return;
        if (t==1){updata(u,gr[1]);return;}
        l=2;r=t;ci=1;
        while(l<=r){
            mid=(l+r)>>1;
            if (cmp(getf(mid),pi[u])>=0){ci=mid;l=mid+1;}
            else r=mid-1;
        }updata(u,gr[ci]);}
    void work(int r,int u){
        int i,j,v;fb[u]=true;
        mx=siz[r]-siz[u];getrt(r,0,mx);
        if (r!=u) work(r,rt); t=zh[0]=0;
        for (i=po[u];i;i=next[i])
            if (!vi[v=en[i]]) dfs(v,u,u);
        sort(zh+1,zh+zh[0]+1,mcm);
        for (j=u,i=1;i<=zh[0];++i){
            while(dep[j]>=dep[r]&&dep[u]-dep[j]<=lm[zh[i]]){
                while(t>1&&cmp(cross(gi[j]-gi[gr[t-1]],gi[gr[t]]-gi[gr[t-1]]),0.)<=0) --t;
                gr[++t]=j;j=fa[j];
            }ask(zh[i]);
        }vi[u]=true;
        for (i=po[u];i;i=next[i]){
            if (fb[v=en[i]]) continue;
            mx=siz[v];getrt(v,0,mx);work(v,rt);
        }
    }
    int main(){
        int i;LL vv;scanf("%d%d",&n,&i);
        for (i=2;i<=n;++i){
            scanf("%d%I64d%I64d%I64d%I64d",&fa[i],&vv,&pi[i],&qi[i],&lim[i]);
            add(fa[i],i,vv);
        }pre(1,0);mx=n;getrt(1,0,mx);
        memset(fi,127/3,sizeof(fi));dep[0]=-1;
        fi[1]=0LL;gi[1]=(point){0.,0.};work(1,rt);
        for (i=2;i<=n;++i) printf("%I64d
    ",fi[i]);
    }
    View Code

    bzoj2244 拦截导弹

    题目大意:给定n个导弹,要求满足三维偏序,求最多拦截几个,每个导弹被拦截的概率.

    思路:cdq分治.做两遍,做出一个点前后最长长度和方案数.solve(l,r)用l~mid更新mid+1~r,按h排序之后,对v做线段树,求min和min的方案.一个点的概率是左边的方案数*右边的方案数/总的方案数.

    写归并排序和树状数组会快很多.

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 50005
    #define LD double
    using namespace std;
    int bi[N],bz=0,tt=0;
    struct use{
        int k,f;LD v;
        void clear(){if (k!=tt){f=0;v=0.;k=tt;}}
    }tr[N<<2];
    struct uu{int h,v,pf,sf;LD p,s;}ai[N];
    struct ui{
        int h,v,k,po;
        bool operator<(const ui&x)const{return (h==x.h ? k>x.k : h>x.h);}
    }ci[N];
    int cmp(const ui&x,const ui&y){return (x.h==y.h ? x.k>y.k : x.h<y.h);}
    use updata(use x,use y){
        x.clear();y.clear();
        if (y.f>x.f){x.f=y.f;x.v=y.v;}
        else if (y.f==x.f) x.v+=y.v;
        return x;}
    void tch(int i,int l,int r,int x,int y,LD z){
        tr[i].clear();
        if (l==r){
            if (y>tr[i].f){
                tr[i].f=y;tr[i].v=z;
            }else if (y==tr[i].f) tr[i].v+=z;
            return;
        }int mid=(l+r)>>1;
        if (x<=mid) tch(i<<1,l,mid,x,y,z);
        else tch(i<<1|1,mid+1,r,x,y,z);
        tr[i]=updata(tr[i<<1],tr[i<<1|1]);}
    use ask(int i,int l,int r,int ll,int rr){
        tr[i].clear();
        if (ll<=l&&r<=rr) return tr[i];
        int mid=(l+r)>>1;use x,y;
        bool f1,f2;f1=f2=false;
        if (ll<=mid){f1=true;x=ask(i<<1,l,mid,ll,rr);}
        if (rr>mid){f2=true;y=ask(i<<1|1,mid+1,r,ll,rr);}
        if (f1&&f2) return updata(x,y);
        if (f1) return x;
        return y;}
    void solve1(int l,int r){
        if (l==r) return;
        int i,j,cz=0,mid;mid=(l+r)>>1;
        solve1(l,mid);
        for (i=l;i<=mid;++i) ci[++cz]=(ui){ai[i].h,ai[i].v,1,i};
        for (;i<=r;++i) ci[++cz]=(ui){ai[i].h,ai[i].v,0,i};
        use cc;sort(ci+1,ci+cz+1);++tt;
        for (i=1;i<=cz;++i){
            j=ci[i].po;
            if (ci[i].k) tch(1,1,bz,ci[i].v,ai[j].pf,ai[j].p);
            else{
                cc=ask(1,1,bz,ci[i].v,bz);
                if (cc.f+1>ai[j].pf){
                    ai[j].pf=cc.f+1;
                    ai[j].p=cc.v;
                }else if (cc.f+1==ai[j].pf) ai[j].p+=cc.v;
            }
        }solve1(mid+1,r);}
    void solve2(int l,int r){
        if (l==r) return;
        int i,j,cz=0,mid;mid=(l+r)>>1;
        solve2(mid+1,r);
        for (i=l;i<=mid;++i) ci[++cz]=(ui){ai[i].h,ai[i].v,0,i};
        for (;i<=r;++i) ci[++cz]=(ui){ai[i].h,ai[i].v,1,i};
        use cc;sort(ci+1,ci+cz+1,cmp);++tt;
        for (i=1;i<=cz;++i){
            j=ci[i].po;
            if (ci[i].k) tch(1,1,bz,ci[i].v,ai[j].sf,ai[j].s);
            else{
                cc=ask(1,1,bz,1,ci[i].v);
                if (cc.f+1>ai[j].sf){
                    ai[j].sf=cc.f+1;
                    ai[j].s=cc.v;
                }else if (cc.f+1==ai[j].sf) ai[j].s+=cc.v;
            }
        }solve2(l,mid);}
    int main(){
        int i,j,n,mx=0;LD sm=0.;scanf("%d",&n);
        for (i=1;i<=n;++i){
            scanf("%d%d",&ai[i].h,&ai[i].v);
            bi[++bz]=ai[i].v;
            ai[i].pf=ai[i].sf=1;
            ai[i].p=ai[i].s=1.;
        }sort(bi+1,bi+bz+1);
        bz=unique(bi+1,bi+bz+1)-bi-1;
        for (i=1;i<=n;++i)
            ai[i].v=upper_bound(bi+1,bi+bz+1,ai[i].v)-bi-1;
        solve1(1,n);
        solve2(1,n);
        for (i=1;i<=n;++i){
            if (ai[i].pf>mx){
                mx=ai[i].pf;sm=ai[i].p;
            }else if (ai[i].pf==mx) sm+=ai[i].p;
        }printf("%d
    ",mx);
        for (i=1;i<=n;++i){
            if (ai[i].pf+ai[i].sf-1<mx) printf("%.5f ",0.);
            else printf("%.5f ",ai[i].p/sm*ai[i].s);
        }printf("
    ");
    }
    View Code
  • 相关阅读:
    第六周例行报告
    第五周每周例行报告
    第三、四周例行报告
    PSP总结报告
    获奖感言
    20181204-1 每周例行报告
    20181127-2 每周例行报告
    软件工程原则的应用实例分析
    20181120-1 每周例行报告
    20181113-2 每周例行报告
  • 原文地址:https://www.cnblogs.com/Rivendell/p/5198684.html
Copyright © 2011-2022 走看看