920考试题解

Cleaning Up 打扫卫生

时间限制: 1 Sec  内存限制: 128 MB

Description

有N头奶牛,每头那牛都有一个标号Pi,1 <= Pi <= M <= N <= 40000。现在Farmer John要把这些奶牛分成若干段,定义每段的不河蟹度为:若这段里有k个不同的数,那不河蟹度为k*k。那总的不河蟹度就是所有段的不河蟹度的总和。

Input

第一行:两个整数N,M

第2..N+1行:N个整数代表每个奶牛的编号

Output

一个整数,代表最小不河蟹度

Sample Input

13 4
1
2
1
3
2
2
3
4
3
4
3
1
4

Sample Output

11
 
题解
      第一遍做的时候只想到一个空间n^2时间n^3的区间dp做法,反复算了内存之后还是没有别的思路。除了勉强打好像没有别的选择,时间花得不算少,就暂且把暴力放下去做别的题了。后来回来再看,感觉空间上是可以O(n)的,内存的问题解决了之后时间也很好办,再稍微加一点剪枝,最后居然拿到了80分,远超原本的期望了。考试毕竟是个过程,更好的思想往往在锤炼比较之后得到。
       正解把第二层n优化成了根号n,但是思路却并不是简单地优化枚举。枚举[l,r]内数字种类,f(i)=min{f(j)+sum(j+1,i)2}(1<=j<i) 想要更优一定不能超过根号(r-l)。预处理pre、nxt数组表示同种数字的前后位置,pos[j]表示[pos[j]+1,i]共有j种数字,

f(i)=min{f(pos(j))+j∗j}(0<=j<=n√)。为了维护pos,用cnt数组记录[pos[j],i]一共有多少种颜色,当i从i+1转移到时候可以通过判断i的pre来更新cnt,如果cnt(j)>j即不合法,pos需要向后移动直到通过nxt某种颜色被完全删除出区间。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int sj=40010;
int n,m,cnt[sj],f[sj],pos[sj],nxt[sj],pre[sj],a1;
void bj(int &x,int y)
{
     x=x<y?x:y;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {  
      scanf("%d",&a1);
      nxt[cnt[a1]]=i,pre[i]=cnt[a1];
      cnt[a1]=i,nxt[i]=n+1;
      f[i]=i;
    }
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=n;i++)
      for(int j=1;j<=sqrt(n);j++)
      {
        if(pre[i]<=pos[j])  cnt[j]++;
        while(cnt[j]>j)
        {
           pos[j]++;
           if(nxt[pos[j]]>i)  cnt[j]--;
        }
        bj(f[i],f[pos[j]]+j*j);
      }
    printf("%d",f[n]);
    return 0;
}
clean

 

 

覆盖问题

时间限制: 1 Sec  内存限制: 256 MB

Description

某人在山上种了N棵小树苗。冬天来了,温度急速下降,小树苗脆弱得不堪一击,于是树主人想用一些塑料薄
膜把这些小树遮盖起来,经过一番长久的思考,他决定用3个L*L的正方形塑料薄膜将小树遮起来。我们不妨将山建
立一个平面直角坐标系,设第i棵小树的坐标为(Xi,Yi),3个L*L的正方形的边要求平行与坐标轴,一个点如果在
正方形的边界上,也算作被覆盖。当然,我们希望塑料薄膜面积越小越好,即求L最小值。

Input

第一行有一个正整数N,表示有多少棵树。接下来有N行,第i+1行有2个整数Xi,Yi,表示第i棵树的坐标,保证
不会有2个树的坐标相同。

Output

一行,输出最小的L值。

Sample Input

4
0 1
0 -1
1 0
-1 0

Sample Output

1

HINT

100%的数据,N<=20000

 

题解

        一个思路诡异的搜索题……并没有看出来它是个搜索,虽然我诚心诚意地打了二分又用一种奇奇怪怪的方式强行check,知道在边界上会出些问题,但是没办法解决就由他去了,没想到真的只有10分。

       为什么是三个正方形?因为矩形有四条边!在任何情况下正方形再不济,也要贴着最小覆盖矩形的一个角。所以枚举它在四个角上的情况,标记哪些点被覆盖再进行下一层dfs,在下一层仍是最小覆盖矩形的方法,到三层后检查是否所有点都被覆盖。代码的实现本身毫无难度,关键在于发现一些必要的规律找到解题的方法。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int sj=20005;
int n,ge;
long long zj,yj,ax,ix,ay,iy,mid;
bool op;
struct point 
{
    long long x,y;
}p[sj];
bool fg[sj],qk;
bool dfs(int st)
{
     qk=1;
     for(int i=1;i<=n;i++)
       if(!fg[i])
       {
          qk=0;
          break;
       }
     if(qk)  return 1;
     if(st>3)  return 0;
     long long zb,xb,sb,yb;
     zb=xb=0x7fffffff,sb=yb=-0x7fffffff;
     bool zt[sj];
     memcpy(zt,fg,sizeof(fg));
     for(int i=1;i<=n;i++)
       if(!fg[i])
       {
          if(p[i].x<zb)  zb=p[i].x;
          if(p[i].x>yb)  yb=p[i].x;
          if(p[i].y>sb)  sb=p[i].y;
          if(p[i].y<xb)  xb=p[i].y;
       }
     for(int i=1;i<=n;i++)
       if(!fg[i]&&p[i].y>=sb-mid&&p[i].x<=zb+mid)
         fg[i]=1;
     if(dfs(st+1))  return 1;
     memcpy(fg,zt,sizeof(zt));
     for(int i=1;i<=n;i++)
       if(!fg[i]&&p[i].y<=xb+mid&&p[i].x<=zb+mid)
         fg[i]=1;
     if(dfs(st+1))  return 1;
     memcpy(fg,zt,sizeof(zt));
     for(int i=1;i<=n;i++)
       if(!fg[i]&&p[i].y>=sb-mid&&p[i].x>=yb-mid)
         fg[i]=1;
     if(dfs(st+1))  return 1;
     memcpy(fg,zt,sizeof(zt));
     for(int i=1;i<=n;i++)
       if(!fg[i]&&p[i].x<=xb+mid&&p[i].x>=yb-mid)
         fg[i]=1;
     if(dfs(st+1))  return 1;
     return 0;
}
int main()
{
    scanf("%d",&n);
    ix=iy=0x7fffffff;
    ax=ay=-0x7fffffff;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&p[i].x,&p[i].y);
        if(p[i].x<ix) ix=p[i].x;
        if(p[i].y<iy) iy=p[i].y;
        if(p[i].x>ax) ax=p[i].x;
        if(p[i].y>ay) ay=p[i].y;
    }
    zj=0,yj=ax-ix;
    if(ay-iy>yj) yj=ay-iy;
    while(zj<yj)
    {
       memset(fg,0,sizeof(fg));
       mid=(zj+yj)>>1;
       if(dfs(1)) yj=mid;
       else zj=mid+1;
    }
    printf("%lld",zj);
    return 0;
}
cover

 

 

高速公路

时间限制: 2 Sec  内存限制: 256 MB

Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。
Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1~N,从收费站i行驶到i+1(或从i+1行驶到i)需要收取Vi的费用。高速路刚建成时所有的路段都是免费的。
政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。
无聊的小A同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的l,r(l<r),在第l个到第r个收费站里等概率随机取出两个不同的收费站a和b,那么从a行驶到b将期望花费多少费用呢?

Input

第一行2个正整数N,M,表示有N个收费站,M次调整或询问
接下来M行,每行将出现以下两种形式中的一种
C l r v 表示将第l个收费站到第r个收费站之间的所有道路的通行费全部增加v
Q l r   表示对于给定的l,r,要求回答小A的问题
所有C与Q操作中保证1<=l<r<=N

Output

对于每次询问操作回答一行,输出一个既约分数
若答案为整数a,输出a/1

Sample Input

4 5
C 1 4 2
C 1 2 -1
Q 1 2
Q 2 4
Q 1 4

Sample Output

1/1
8/3
17/6

HINT

数据规模
所有C操作中的v的绝对值不超过10000
在任何时刻任意道路的费用均为不超过10000的非负整数
所有测试点的详细情况如下表所示
Test N M
1 =10 =10
2 =100 =100
3 =1000 =1000
4 =10000 =10000
5 =50000 =50000
6 =60000 =60000
7 =70000 =70000
8 =80000 =80000
9 =90000 =90000
10 =100000 =100000

 

题解

         很明显的每个收费站对答案的贡献为v[i]*(i-l+1)*(r-i+1),然而我考试的时候一直想着怎样维护整个的答案,想都没想就开始线段树。线段树越打越烦躁,感觉根本就没有办法维护出来,干脆弃了线段树去写裸暴力。但是尽管它是个n^无数的裸暴力我也没想到居然能全T掉,这就很尴尬了,按照题目中给的范围不至于啊……

      把第一行那个式子拆开,得到

Σ(i=l,r) -i^2 *a[i]+i*a[i]*(l+r)-(r+1)*(l-1)*a[i]=-Σ(i=l,r)-i^2 *a[i]+(l+r)Σ(i=1,r)i*a[i]-(r+1)*(l-1)Σ(i=l,r)a[i],这样不就可以分别维护i*i*a[i],i*a[i],a[i]的区间和了。如果过去线段树的很多题中心都在于怎样维护,这道题也提供了一个关于“维护什么”的新思路。只要有时间就把式子拆一拆,有益解题有益身心健康啊……

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int sj=100010;
int n,m;
long long ans,a1,a2,tp,gcd,a3;
char a;
struct Tree
{
    long long fa,ya,aa,ia,pa,l,r;
}t[sj*4];
long long lazy[sj*4],len,ans1,ans2,ans3;
long long mgcd(long long x,long long y)
{
     if(y==0) return x;
     return mgcd(y,x%y);
}
void build(int x,long long z,long long y)
{
     t[x].l=z,t[x].r=y;
     if(z==y) 
     {
       t[x].ia=y;
       t[x].pa=y*y;
       return;
     }
     int mid=(z+y)>>1;
     build(x<<1,z,mid),build((x<<1)|1,mid+1,y);
     t[x].ia=t[x<<1].ia+t[(x<<1)|1].ia;
     t[x].pa=t[x<<1].pa+t[(x<<1)|1].pa;
}
void pushup(int x)
{
     t[x].aa=t[x<<1].aa+t[(x<<1)|1].aa;
     t[x].ya=t[x<<1].ya+t[(x<<1)|1].ya;
     t[x].fa=t[x<<1].fa+t[(x<<1)|1].fa;
}
void pushdown(int x)
{
     if(lazy[x])
     {
         int ls=x<<1,rs=(x<<1)|1;
         lazy[ls]+=lazy[x],lazy[rs]+=lazy[x];
         len=t[rs].r-t[rs].l+1ll;
         t[rs].aa+=len*lazy[x],t[rs].ya+=t[rs].ia*lazy[x],t[rs].fa+=t[rs].pa*lazy[x];
         len=t[ls].r-t[ls].l+1;
         t[ls].aa+=len*lazy[x],t[ls].ya+=t[ls].ia*lazy[x],t[ls].fa+=t[ls].pa*lazy[x];
         lazy[x]=0;
     }
}
void update(int x,long long z,long long y,long long v)
{
     if(t[x].l==z&&t[x].r==y)  
     {
         lazy[x]+=v;
         len=y-z+1;
         t[x].aa+=len*v;
         t[x].ya+=t[x].ia*v;
         t[x].fa+=t[x].pa*v;
         return;
     }
     long long mid=(t[x].r+t[x].l)>>1;
     pushdown(x);
     if(y<=mid)  update(x<<1,z,y,v);
     if(z>mid)   update((x<<1)|1,z,y,v);
     if(z<=mid&&y>mid)
     {
        update(x<<1,z,mid,v);
        update((x<<1)|1,mid+1,y,v);
     }
     pushup(x);
}
void query(int x,long long z,long long y)
{
     if(t[x].l==z&&t[x].r==y)
     {
        ans1=t[x].aa,ans2=t[x].ya,ans3=t[x].fa;
        return;
     }
     long long mid=(t[x].l+t[x].r)>>1;
     pushdown(x);
     if(y<=mid)
     { 
       query(x<<1,z,y);
       return;
     }
     if(z>mid)
     {   
       query((x<<1)|1,z,y);
       return;
     }
     query(x<<1,z,mid);
     long long ta1,ta2,ta3;
     ta1=ans1,ta2=ans2,ta3=ans3;
     query((x<<1)|1,mid+1,y);
     ans1+=ta1,ans2+=ta2,ans3+=ta3;
}
int main()
{
    scanf("%d%d",&n,&m);
    n--;
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
       scanf("%s%lld%lld",&a,&a1,&a2);
       a2--;
       if(a=='C')
       {
          scanf("%lld",&a3);
          update(1,a1,a2,a3);
       }
       if(a=='Q')
       {
          query(1,a1,a2);
          ans=ans2*(a1+a2)-ans3-(a2+1ll)*(a1-1ll)*ans1;
          tp=(a2-a1+2)*(a2-a1+1)/2;
          gcd=mgcd(ans,tp);
          ans/=gcd;
          tp/=gcd;
          printf("%lld/%lld\n",ans,tp);
       }
    }
    return 0;
}
road

 

posted @ 2017-09-24 20:17  moyiii  阅读(245)  评论(0编辑  收藏  举报