2019年7月25日(思维)

有人说我\(fake\),不开心!!

时间不多了,就算还是水得一批我也没时间抱怨了,讲题!

prob1:A

题目大意:一棵有n个节点的树,当且仅当一个节点与他的某祖先距离小于其点权时对其祖先有1点贡献,求每点最后得到贡献

终于出一道人性的\(T1\)了,这题明显与天天爱跑步差不多,一个点\(i\)能被其子树上一点\(j\)贡献一次当且仅当\(dep[j]-dep[i]<=w[j]\),移项得:\(dep[j]-w[j]<=dep[i]\)。采用相同思想,先将权值离散化,使用树状数组\(cnt[i]\)维护小于等于\(i\)的数的个数。每次访问到一个点\(i\),先用\(before\)存储当时的\(cnt[dep[i]]\),再遍历其子树,再用\(then\)存储现在\(cnt[dep[i]]\),二者之差则为当前点答案,再将\(cnt[dep[i]-w[i]]\)进行加一修改(因为题中要求该子树不包括该点)。最后输出答案。

标程算法为差分加倍增跳可达祖先,同样很强

总之水题一道,因为数组开小和没开好\(long long\)而萎掉,很难过

看代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define pl pair<ll,ll>
#define xx 220000
#define nm make_pair
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct point{ll fa,w,dis;}dot[xx];
vector<pl> e[xx];
ll cnt[xx<<1],ans[xx],n;
ll all=0,res=0,v1[xx<<1],v2[xx<<1];
inline int lowbit(int i){return i&-i;}
inline void add(int i,ll x)
{
    if(!i) return;
    while(i<=n)
    {
        cnt[i]+=x;
        i+=lowbit(i);
    }
}
inline int ask(int i)
{
    int now=0;
    while(i>0)
    {
        now+=cnt[i];
        i-=lowbit(i);
    }
    return now;
}
inline void dfs1(int g)
{
    fur(i,0,(int)e[g].size()-1)
    {
        dot[e[g][i].first].dis=dot[g].dis+e[g][i].second;
        dfs1(e[g][i].first);
    }
}
inline void dfs2(int g)
{
    ll before=ask(dot[g].dis);
    fur(i,0,(int)e[g].size()-1) dfs2(e[g][i].first);
    ll then=ask(dot[g].dis);
    add(dot[g].w,1ll);
    ans[g]=then-before;
}
inline int look(ll k)
{
    int h=1,t=all;
    while(h<=t)
    {
        int mid=(h+t)>>1;
        if(k<v2[mid]) t=mid-1;
        else if(k>v2[mid]) h=mid+1;
        else return mid;
    }
}
int main()
{
    n=in;
    fur(i,1,n) dot[i].w=in;
    fur(i,2,n)
    {
        dot[i].fa=in;
        ll x=in;
        e[dot[i].fa].push_back(nm(i,x));
    }
    dfs1(1);
    fur(i,1,n)
    {
        v1[i]=dot[i].dis-dot[i].w;
        v1[n+i]=dot[i].dis;
    }
    sort(v1+1,v1+n*2+1);
    fur(i,1,2*n)
    {
        while(v1[i]==v1[i+1]) i++;
        v2[++all]=v1[i];
    }
    fur(i,1,n)
    {
        dot[i].w=look(dot[i].dis-dot[i].w);
        dot[i].dis=look(dot[i].dis);
    }
    n*=2;
    dfs2(1);
    fur(i,1,n/2) printf("%lld ",ans[i]);
    printf("\n");
    return 0;
}

prob2:B

题目大意:求三组排列中使得\(a_i<a_j,b_i<b_j,c_i<c_j\)的点对\((i,j)\)组数

题目安排的比较\(fake\),乍一看还以为是\(cdq\),结果一看\(2*1e6\),炸了,打了个70分\(cdq\)就跑了。

正解要用容斥,

\(kuai\)一下\(lst\)学姐的题解 :


\(S(i,j)=[A_i>A_j]+[B_i>B_j]+[C_i>C_j]\)

\(M(i,j)=max(S(i,j),S(j,i))\)

可以发现M(i,j)要么是\(2\)要么是\(3\),设\(M(i,j)=2\)的数目为\(a\),设\(M(i,j)=3\)的数目为\(b\)
\(a+b=C_n^2\)

分别求出\(A_i>A_j\)&&\(B_i>B_j\),\(A_i>A_j\)&&\(C_i>C_j\),\(C_i>C_j\)&&\(B_i>B_j\)的数目后相加,可以发现就是\(3b+a\)


综上所述:二者一减除以二即为答案

程序:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define ll int
#define xx 2000010
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
long long ans,seed,n;
ll cnt1[xx],cnt2[xx],cnt3[xx],a[xx],b[xx],c[xx];
#define fuck 19260817
#define cow 233333
#define xie (1<<24)-1
inline ll Ran()
{
    return seed=((fuck*seed)^cow)&xie;
}
inline void gen(ll *A)
{
    fur(i,1,n) A[i] = i;
    fur(i,1,n) swap(A[i],A[Ran()%i+1]);
}
inline int lowbit(int i){return i&-i;}
inline void add(int i,ll x,ll *cnt){while(i<=n){cnt[i]+=x;i+=lowbit(i);}}
inline int ask(int i,ll *cnt){ll res=0;while(i>0){res+=cnt[i];i-=lowbit(i);}return res;}
struct man{ll x,y;}g1[xx],g2[xx],g3[xx];
inline bool cmp(man f,man s){return f.x<s.x;}
int main()
{
    n=in;
    seed=in;gen(a);
    seed=in;gen(b);
    seed=in;gen(c);
    ans=-(n*(n-1)>>1);
    fur(i,1,n)
    {
        g1[i]=(man){a[i],b[i]};
        g2[i]=(man){b[i],c[i]};
        g3[i]=(man){a[i],c[i]};
    }
    sort(g1+1,g1+n+1,cmp);
    sort(g2+1,g2+n+1,cmp);
    sort(g3+1,g3+n+1,cmp);
    fur(i,1,n)
    {
        add(g1[i].y,1,cnt1);
        ans+=ask(g1[i].y-1,cnt1);
    }
    fur(i,1,n)
    {
        add(g2[i].y,1,cnt2);
        ans+=ask(g2[i].y-1,cnt2);
    }
    fur(i,1,n)
    {
        add(g3[i].y,1,cnt3);
        ans+=ask(g3[i].y-1,cnt3);
    }
    printf("%lld\n",ans>>1);
    return 0;
}

prob3:C

题目大意:\(luoguP4823\)拯救小矮人

\(sb\)贪心做法:考试数据\(90\)\(luogu\)数据\(30\)

先找在当前身高加手臂长能搞事情的,压进一个小根堆,找一个身高最矮的,让他搞事情,再把他给弃掉,直到搞不了事情为止

上代码:

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define pl pair<ll,ll>
#define nm make_pair
#define xx 4001
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct heep
{
    priority_queue<pl,vector<pl>,greater<pl> >q;
    inline void ad(ll x,int i)
        {
            q.push(nm(x,i));
        }
    inline int top()
        {
            return q.top().second;
        }
    inline void clear()
        {
            while(!q.empty()) q.pop();
        }
}p;
ll a[xx],b[xx],ans=0;
bool choose[xx];
int main()
{
    ll n=in,sum=0,c=0;
    fur(i,1,n)
    {
        a[i]=in,b[i]=in;
        sum+=a[i];
    }
    ll h=in;
    fur(i,1,n)
    {
        if(b[i]+sum>=h)
        {
            p.ad(a[i],i);
            c=max(c,b[i]);
        }
    }
    while(sum+c>=h)
    {
        ++ans;
        sum-=a[p.top()];
        choose[p.top()]=true;
        p.clear();
        c=0;
        fur(i,1,n)
        {
            if(!choose[i])
            {
                if(b[i]+sum>=h)
                {
                    p.ad(a[i],i);
                    c=max(c,b[i]);
                }
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}

\(O2\)的话时间没问题,但会莫名\(WA\)

正解:按身高加手臂长排序,总和小的必会先被弃掉,因为没用,再用\(f[i]\)存储当走了\(i\)个人时所形成的最高人梯高度,进行状态转移

具体看代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define fdr(i,a,b) for(int i=a;i>=b;--i)
#define ll long long
#define xx 4001
inline int read()
{
    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct link{ll a,b;}x[xx];
ll f[xx];
inline bool cmp(link q1,link q2){return q1.a+q1.b<q2.a+q2.b;}
int main()
{
    int n=in;
    fur(i,1,n)
    {
        x[i].a=in;
        x[i].b=in;
        f[i]=-1;
        f[0]+=x[i].a;
    }
    int h=in;
    sort(x+1,x+n+1,cmp);
    fur(i,1,n) fdr(j,i,1) if(f[j-1]+x[i].b>=h) f[j]=max(f[j],f[j-1]-x[i].a);
    fdr(i,n,0)
    {
        if(f[i]>=0)
        {
            printf("%d\n",i);
            return 0;
        }
    }
    return 0;
}

prob4:D

题目大意:目标点等概率地落在了一棵树的叶子节点上,有的节点上有提示说明该节点的子树上是否存在目标点,求到达目标点的最优期望

期望\(DP\)建议这题入门

\(F[i]\)为在\(i\)节点出发而在\(i\)节点子树找到目标点期望步数,设\(G[i]\)为未找到而返回\(i\)节点的期望步数,\(cnt[i]\)\(i\)节点子树上的叶子节点个数。则\(F[i]=\Sigma(F[son[i][j]]*cnt[son[i][j]]/cnt[i]+G[son[i][j]]*(cnt[i]-\Sigma_q^{j-1}cnt[son[i][q]])/cnt[i])\)
\(G[i]=\Sigma{G[son[i][j]]/cnt[i]}\)
题中\(G\)数组为了存值和转移方便,仅保留公式中分子
详见代码:

#include<iostream>
#include<vector>
#include<cstdio>
#include<algorithm>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i+=1)
#define ll long long
#define xx 1100
inline int read()
{
    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
vector<int>e[xx];
bool snail[xx];
double g[xx],f[xx],cnt[xx];
inline void init(int n)
{
    fur(i,1,n) e[i].clear(),snail[i]=false;
    fur(i,1,n) g[i]=f[i]=0;
}
inline bool cmp(int x,int y){return g[x]/cnt[x]<g[y]/cnt[y];}
inline void dfs(int j)
{
    if((int)e[j].size()==0)
    {
        g[j]=f[j]=0;
        cnt[j]=1;
        return;
    }
    int son[xx],len=0;
    cnt[j]=0;
    fur(i,0,(int)e[j].size()-1)
    {
        dfs(e[j][i]);
        son[++len]=e[j][i];
        f[e[j][i]]+=1;
        g[e[j][i]]+=2;
        cnt[j]+=cnt[e[j][i]];
    }
    sort(son+1,son+len+1,cmp);
    double sum=0;
    fur(i,1,len)
    {
        sum+=cnt[son[i]];
        f[j]+=f[son[i]]*cnt[son[i]]+g[son[i]]*(cnt[j]-sum);
        g[j]+=g[son[i]];
    }
    f[j]/=cnt[j];
    if(snail[j]) g[j]=0;
}
int main()
{
    int t=in;
    while(t--)
    {
        int n=in;
        fur(i,1,n)
        {
            int x=in;
            if(x>0) e[x].push_back(i);
            char op[2];
            scanf("%s",op);
            if(op[0]=='Y') snail[i]=true;
        }
        dfs(1);
        printf("%.4lf\n",f[1]);
        init(n);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ALANALLEN21LOVE28/p/11312986.html