GKurumi记

旧巷老猫 提交于 2019-11-30 18:59:34

20191008陈蒟蒻爆0记

T2、City

by GKurumi

题目描述:(加密)

这个题一开始看起来冗杂,之后我们考虑一下,边的话基本可以处理成一个定值,因为没有边的城市建边一定是最少人的时候建边人数最少。

之后思考一个结论,假设有城市x,y对应bx,by,hx,hy需要在这两个城市各建若干个房子,那么我们一个一个城市分别完成一定比交错完成优。

证明先建大的最优:

假设hx>hy,那么先在x建应比在y建优,如果每一个城市都只要建两所房子的话:

hx(bx+by)+hy(bx+by+1)+hx(bx+by+2)+hy(bx+by+3)

hx(bx+by)+hx(bx+by+1)+hy(bx+by+2)+hy(bx+by+3)

约掉同类项:

有(2hy+hx)−(hy+2hx)=hy−hx<0成立。

需要建两个房子以上的时候可以归纳。

之后我们用最小生成树

计算边权的时候如果有边:\(h[x]\times b[y]\times (a[x]-b[x])\)

如果没有边是,需在基础上加上修路钱:\(R\times(b[x]+b[y])\)

建好之后就可以跑最小生成树了。

上代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
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<<1)+(x<<3)+(ch-'0');ch=getchar();}
    return x*f;
}
int n,fa[10005],a[10005],b[10005],h[100005],R,mp[1005][1005],ans,ecnt;

struct node
{
    int u,v;
    long long val;
}e[2505];
int cnt;
void add(int u,int v,int val)
{
    e[++cnt].u=u;
    e[cnt].v=v;
    e[cnt].val=val;
}
bool cmp(node a,node b)
{
    return a.val<b.val;
}

int find(int x)
{
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}

signed main()
{
//  freopen("city.in","r",stdin);
//  freopen("city.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++) fa[i]=i; 
    for(int i=1;i<=n;i++) b[i]=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++) h[i]=read();
    char st[10005];
    for(int i=1;i<=n;i++)
    {
        
        cin>>st+1;
        for(int j=1;j<=n;j++)
        {
            if(st[j]=='N')
            {
                mp[i][j]=0;
            }
            else mp[i][j]=1;
        }
    }
    R=read();
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<i;j++)
        {
            if(mp[i][j])
            {
                if(h[i]<h[j])//先算hj 
                {
                    ans+=h[j]*b[i]*(a[j]-b[j])+h[i]*a[j]*(a[i]-b[i]);
                }
                else
                {
                    ans+=h[i]*b[j]*(a[i]-b[i])+h[j]*a[i]*(a[j]-b[j]);
                }
                int x=find(i),y=find(j);
                if(x==y)continue;
                fa[x]=y;
                ++ecnt;
            }
            else
            {
                if(h[i]<h[j])
                {
                    add(i,j,((h[j]*b[i]*(a[j]-b[j])+h[i]*a[j]*(a[i]-b[i]))+R*(b[i]+b[j])));
                }
                else
                {
                    add(i,j,((h[i]*b[j]*(a[i]-b[i])+h[j]*a[i]*(a[j]-b[j]))+R*(b[i]+b[j])));
                }
            }
        }
    }
    sort(e+1,e+1+cnt,cmp);
    for(int i=1;i<=cnt&&ecnt<n-1;i++)
    {
        int u=find(e[i].u),v=find(e[i].v);
        if(u==v)continue;
        fa[u]=v;
        ans+=e[i].val;
        ++ecnt;
    }
    for(int i=1;i<=n;i++)
    {
        ans+=h[i]*(a[i]-b[i])*(b[i]+a[i]-1)/2;
    }
    cout<<ans;
    return 0;
}
/*
4
1 1 1 1
1 3 1 2
8 5 3 2
NYNN
YNYN
NYNY
NNYN
100000
*/      

最后要注意最小生成树算完还差点火候,就是在最后,因为对于每一个点x,

它对答案的贡献是:\(hx(bx+(bx+1)+(bx+2)+...+(ax−1))+hx∗(sum(y))\)

y是x能直接联通的点。所以我们需要在处理处理一个等差数列相加。

就完美了。

如有转载,请标明原地址出处,原地址链接

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!