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

来源:互联网 发布:大都会歌剧院 知乎 编辑:程序博客网 时间:2024/06/08 08:14

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;}
原创粉丝点击