欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

NOIP2017提高组 模拟赛18(总结)

程序员文章站 2022-03-14 19:22:02
...

NOIP2017提高组 模拟赛18(总结)

第一题 火柴

【题目描述】

【解题思路】

  从左往右扫一遍,优先满足第i位,若ai>ave,则将多余的火柴移到第i+1位;若ai<ave,则将i+1位的ave-ai个移到第i位(即使将第i+1位变成负数也没关系,因为第i+1位也可以从后面拿),统计移动次数即可。

【代码】

#include<cstdio>
#include<algorithm>

using namespace std;

typedef long long ll;

const int N=50050;
ll d[N],sum,ans;
int n;

int main()
{
    freopen("2247.in","r",stdin);
    freopen("2247.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&d[i]),sum+=d[i];
    sum=sum/n;
    for(int i=1;i<n;i++)
    {
        if(d[i]>sum) d[i+1]+=d[i]-sum,ans+=d[i]-sum,d[i]=sum;
        else d[i+1]-=sum-d[i],ans+=sum-d[i],d[i]=sum;
    }
    printf("%lld\n",ans);
    return 0;
}

第二题 管理者

【题目描述】

【解题思路】

  先求出最小生成树,假如ui->vi必须升级,则在ui->vi的路径上找出最大的边,替换掉就行了。
  如何快速求路径最大值呢?
  LCT可以很好的解决这一问题(滑稽)
  其实用树上倍增就足够了。

【代码】

#include<cstdio>
#include<algorithm>

#define imax(a,b) ((a>b)?(a):(b))

using namespace std;

typedef long long ll;

const int N=200500;
struct tree
{
    tree *f,*c[2],*pp;
    int val,mx;
    bool flip;
    int d() { return (f->c[1]==this); }
    void sc(tree *x,int d) { (c[d]=x)->f=this; }
} nil[N<<2],*ro[N<<2],*stack[N<<2];
struct data
{
    int l,r,cost,id;
} road[N];
int f[N],n,m,cnt;
ll ans;

void down(tree *x)
{
    if(x==nil) return;
    if(x->flip)
    {
        swap(x->c[0],x->c[1]);
        if(x->c[0]!=nil) x->c[0]->flip^=1;
        if(x->c[1]!=nil) x->c[1]->flip^=1;
        x->flip=0;
    }
}

void up(tree *x)
{
    if(x==nil) return;
    x->mx=x->val;
    if(x->c[0]!=nil) x->mx=imax(x->mx,x->c[0]->mx);
    if(x->c[1]!=nil) x->mx=imax(x->mx,x->c[1]->mx);
}

tree *newtree()
{
    nil[++cnt]=nil[0];
    return nil+cnt;
}

void rotate(tree *x)
{
    int d=x->d();
    tree *y=x->f;
    y->sc(x->c[!d],d);
    if(y->f==nil) x->f=nil; else y->f->sc(x,y->d());
    x->sc(y,!d);
    x->pp=y->pp;
    y->pp=nil;
    up(y); up(x);
}

void splay(tree *x)
{
    int hy=1; stack[hy]=x;
    for(;stack[hy]->f!=nil;hy++) stack[hy+1]=stack[hy]->f;
    for(int i=hy;i>=1;i--) down(stack[i]);
    for(tree *y;x->f!=nil;)
    {
        y=x->f;
        if(y->f!=nil)
        (y->d()^x->d())?rotate(x):rotate(y);
        rotate(x);
    }
}

void access(tree *x)
{
    tree *y=nil;
    while(x!=nil)
    {
        splay(x);
        if(x->c[1]!=nil)
        {
            x->c[1]->f=nil;
            x->c[1]->pp=x;
        }
        x->c[1]=y;
        if(y!=nil)
        {
            y->f=x;
            y->pp=nil;
        }
        up(x);
        y=x;
        x=x->pp;
    }
}

void evert(tree *x)
{
    access(x);
    splay(x);
    x->flip^=1;
}

void link(tree *x,tree *y)
{
    evert(y);
    splay(y);
    y->pp=x;
}

bool cmp(data A,data B) { return (A.cost<B.cost); }
bool cmp1(data A,data B) { return (A.id<B.id); }

int find(int x) { return ((f[x]<0)?(x):(f[x]=find(f[x]))); }

void uni(int now)
{
    int x1=find(road[now].l),x2=find(road[now].r);
    if(x1==x2) return;
    ans+=road[now].cost;
    link(ro[road[now].l],ro[n+road[now].id]);
    link(ro[road[now].r],ro[n+road[now].id]);
    if(f[x1]>f[x2]) swap(x1,x2);
    f[x1]+=f[x2]; f[x2]=x1;
}

int findmax(tree *x,tree *y)
{
    evert(x); access(y);
    splay(y); return y->mx;
}

int main()
{
    freopen("2248.in","r",stdin);
    freopen("2248.out","w",stdout);
    cnt=0; nil->f=nil->pp=nil->c[0]=nil->c[1]=nil;
    nil->mx=nil->val=0; nil->flip=0;
    scanf("%d%d",&n,&m); ans=0ll;
    for(int i=1;i<=n;i++) ro[i]=newtree();
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&road[i].l,&road[i].r,&road[i].cost),road[i].id=i;
        ro[n+i]=newtree();
        ro[n+i]->val=ro[n+i]->mx=road[i].cost;
    }
    sort(road+1,road+1+m,cmp);
    for(int i=1;i<=n;i++) f[i]=-1;
    for(int i=1;i<=m;i++) uni(i);
    sort(road+1,road+1+m,cmp1);
    for(int i=1;i<=m;i++)
    {
        int yu=findmax(ro[road[i].l],ro[road[i].r]);
        printf("%lld\n",ans-yu+road[i].cost);
    }
    return 0;
}

第三题 数学家

【题目描述】

【解题思路】

  记g[h][i]为长度为h的南墙,用i块草坪去铺的最小面积。
  记f[h][i][j]为长度为h的南北墙,北墙用i块草坪,南墙用j块草坪去铺的最小面积。
  g[h][1]=hhf2  g[h][i]=g[hh/i][i1]+(h/i)2f2
  f[h][i][j]=max(f[h1][i1][j1]+g[hh1][jj1]+(hh1)2f1)
  h1<h,j1<j
  时间复杂度:O(N^5)  显然超时……
  其实这具有决策单调性,因为
  假若f[h][i-1][j]的决策是h1,j1,f[h][i][j]的决策是h2,j2,必定存在h2>h1,j2>j1。
  利用这一决策单调性可以除去一维,时间复杂度:O(N^4)
  加了许多优化,程序跑的飞快……
  千万记得判是否越界,例如草坪数要永远不超过墙的长度。
  没有注意,WA了好多次

【代码】

#include<cstdio>
#include<algorithm>

#define imax(a,b) ((a>b)?(a):(b))
#define imin(a,b) ((a<b)?(a):(b))

using namespace std;

typedef long long ll;

const int N=120;
double f1,f2;
int n,m;
struct data { int l,p; } d[N][N][N];
double g[N][N],f[N][N][N];

int main()
{
    freopen("2249.in","r",stdin);
    freopen("2249.out","w",stdout);
    scanf("%lf%lf%d%d",&f1,&f2,&n,&m);
    for(int i=1;i<=100;i++)
    {
        g[i][0]=1e9;
        g[i][1]=i*i;
        for(int j=2;j<=i;j++)
        {
            int yu=i/j;
            g[i][j]=g[i-yu][j-1]+yu*yu;
        }
    }
    for(int i=1;i<=100;i++)
    {
        for(int j=1;j<=m;j++)
        {
            f[i][j][1]=i*i*f1+g[i][j]*f2;
            d[i][j][1].l=0; d[i][j][1].p=0;
        }
    }
    for(int h=2;h<=100;h++)
    {
        int mi=imin(h,m),mj=imin(h,n);
        for(int i=2;i<=mi;i++)
        {
            for(int j=2;j<=imin(i,mj);j++)
            {
                int sh=imax(1,d[h][i][j-1].l),si=imax(imax(1,d[h][i][j-1].p),j-1);
                f[h][i][j]=1e9;
                for(int h1=sh;h1<=h;h1++)
                {
                    /*if(h==100 && i==100 && h1==50)
                    printf("ok\n");*/
                    double getans=1e9; int kl,kp;
                    int gh=h-h1;
                    int ip=imax(i-gh,si),mip=imin(h1,i);
                    for(int i1=ip;i1<=mip;i1++)
                    {
                        double yu=f[h1][i1][j-1]+gh*gh*f1+g[gh][i-i1]*f2;
                        if(yu>getans) break;
                        getans=yu;
                        kl=h1; kp=i1;
                    }
                    if(getans<f[h][i][j])
                    {
                        f[h][i][j]=getans;
                        d[h][i][j].l=kl; d[h][i][j].p=kp;
                    }
                }
                //printf("%d %d %d %.lf\n",h,i,j,f[h][i][j]);
            }
        }
    }
    printf("%.1lf\n",f[100][m][n]);
    return 0;
}