平时二十四测

 

今天又倒数,第二题复制了以后忘改变量名;

第三题文件错误,我竟然也犯了这种错,可能考试将近,心态没调整好;

 

题解:

思路很棒的DP题。

可以发现每一段中不同的数的个数不超过sqrt(n)个。

所以我们只需要记录每个点为结尾,一段序列中不同的数的个数不超过j的左端点的位置-1,pos[j]。通过pos数组进行DP转移,可以将复杂度从O(n^2)将为O(sqrt(n)n)。f[i]=min{f[pos[j]]+j*j}。

下面我们考虑右端点右移,也就是i++时,如何更新pos数组。

为了快速更新,我们还需要记录每个数值i的最后一个位置pre[i],和以pos[j]为左端点的序列中不同的数的个数cnt[j]。

当i++后,如果pre[a[i]]≤pos[j],那么cnt[j]++,说明序列中加入一个新的元素。

然后我们找出所有cnt[j]>j的序列,也就是不满足条件的序列,适当地调整左端点pos[j],使其满足cnt[j]≤j。其中对于左端点的调整只需要暴力即可,这是均摊O(1)的。

所以这道题最终的时间复杂度为O(sqrt(n)n)。

这题n2的暴力非常好想,预处理[l,r]有多少种食物sum(l,r),然后f(i)=min{f(j)+sum(j+1,i)2}(1<=j<i) 
然后利用一点数学知识就有一个非常巧妙的优化 
这道题的答案是不会超过n的,所以要想最优的话,枚举的sum(l,r)不能超过n√

预处理pre(i),nxt(i)表示与位置i食物相同的前一个\下一个的位置 
pos(j)表示[pos(j)+1,i]一共有j种不同的食物 
那么f(i)=min{f(pos(j))+j∗j}(0<=j<=n√)

如何维护pos(j)呢? 
记cnt(j)表示[pos(j),i]一共有多少种颜色 
当i从i-1转移来时可以通过判断位置i的食物的pre来计算cnt(j) 
如果cnt(j)>j即不合法,那么pos(j)要向后移动,直到把某一种颜色在区间中完全删除,这中间可以用nxt来判断

pos(j)单调移动,总时间复杂度O(nn√)

#include<bits/stdc++.h>
using namespace std;

const int M = 4e4 + 10;

int a[M], dp[M], lst[M], cnt[M], n, m, pos[M];

int read(){
    int x = 0; int f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*=f;
}
int main(){
    freopen("cleanup.in","r",stdin);
    freopen("cleanup.out","w",stdout);
    n = read(), m = read();
    int blo = sqrt(n);
    memset(dp, 127, sizeof(dp));
    dp[0] = 0;
    for(int i = 1; i <= n; i++){
        a[i] = read();
        for(int j = 1; j <= blo; j++)
            if(lst[a[i]] <= pos[j]) cnt[j]++;
        lst[a[i]] = i;
        for(int j = 1; j <= blo; j++)
        if(cnt[j] > j){
            int t = pos[j] + 1;
            while(lst[a[t]] > t)t++;
            pos[j] = t; cnt[j]--;
        }
        for(int j = 1; j <= blo; j++) dp[i] = min(dp[i], dp[pos[j]] + j * j);
    }
    printf("%d\n",dp[n]);
}
View Code

 

 

 

T2:

显然是二分+判断可行性.

如何判断可行性呢?我们注意到是3个正方形.为什么是3个?

我们先找出覆盖所有点的最小距形,那么距形的四条边必须有正方形贴着,而又是3个正方形,所以至少要有1个正方形同时贴着两条边.

 

1.贴着的边相对

 

这样的话三个正方形都同时贴着相对的两条边,比如是上下两条边,那么贴着左边的那个正方形就贴着3条边,在距形的一角.

 

2.贴着的边相邻

 

这样的话这个正方形就在距形的一角.

 

所以我们递归的把正方形放在距形的一角,然后去掉已经覆盖了的点,继续放正方形即可.

 

 

#include<bits/stdc++.h>
using namespace std;

const int M = 4e4 + 10, inf = 2e9;
#define RG register
struct Mat{
    int x, y;
}f[4][M], p[M], tmp[M];
inline int max(int a, int b){return a >= b ? a : b;}
inline int min(int a, int b){return a <= b ? a : b;}
bool check(int k, int  upx, int dwx, int lfy, int rgy, int res, int now){
    if(!now)return 1;
    if(res == 1) return max((upx - dwx), (rgy - lfy)) <= k;
    else {
        //zuoxia;
        for(int i = 1; i <= now; i++) f[res][i] = tmp[i];
        
        int nupx = -inf, ndwx = inf, nlfy = inf, nrgy = -inf;
        int zy = lfy, ry = lfy + k, zx = dwx, rx = dwx + k, cnt = 0;
        for(RG int i = 1; i <= now; i++){
            if(f[res][i].x < zx || f[res][i].y < zy || f[res][i].x > rx || f[res][i].y > ry){
                nupx = max(nupx, f[res][i].x), ndwx = min(ndwx, f[res][i].x);
                nlfy = min(nlfy, f[res][i].y), nrgy = max(nrgy, f[res][i].y);
                tmp[++cnt] = f[res][i];
            }
        }
        if(check(k, nupx, ndwx, nlfy, nrgy, res-1, cnt)){
            //printf("%d 1\n",res);
            return 1;    
        }    
        
        nupx = -inf, ndwx = inf, nlfy = inf, nrgy = -inf;
        zy = lfy, ry = lfy + k, zx = upx-k, rx = upx, cnt = 0;
        for(RG int i = 1; i <= now; i++){
            if(f[res][i].x < zx || f[res][i].y < zy || f[res][i].x > rx || f[res][i].y > ry){
                nupx = max(nupx, f[res][i].x), ndwx = min(ndwx, f[res][i].x);
                nlfy = min(nlfy, f[res][i].y), nrgy = max(nrgy, f[res][i].y);
                tmp[++cnt] = f[res][i];
            }
        }
        if(check(k, nupx, ndwx, nlfy, nrgy, res-1, cnt)) {
            //printf("%d 2\n",res);
            return 1;    }
        
        nupx = -inf, ndwx = inf, nlfy = inf, nrgy = -inf;
        zy = rgy-k, ry = rgy, zx = dwx, rx = dwx + k, cnt = 0;
        for(RG int i = 1; i <= now; i++){
            if(f[res][i].x < zx || f[res][i].y < zy || f[res][i].x > rx || f[res][i].y > ry){
                nupx = max(nupx, f[res][i].x), ndwx = min(ndwx, f[res][i].x);
                nlfy = min(nlfy, f[res][i].y), nrgy = max(nrgy, f[res][i].y);
                tmp[++cnt] = f[res][i];
            }
        }
        if(check(k, nupx, ndwx, nlfy, nrgy, res-1, cnt)){
        //    printf("%d 3\n",res);
        return 1;    
        } 
        
        
        nupx = -inf, ndwx = inf, nlfy = inf, nrgy = -inf;
        zy = rgy - k, ry = rgy, zx = upx - k, rx = upx, cnt = 0; 
        for(RG int i = 1; i <= now; i++){
            if(f[res][i].x < zx || f[res][i].y < zy || f[res][i].x > rx || f[res][i].y > ry){
                nupx = max(nupx, f[res][i].x), ndwx = min(ndwx, f[res][i].x);
                nlfy = min(nlfy, f[res][i].y), nrgy = max(nrgy, f[res][i].y);
                tmp[++cnt] = f[res][i];
            }
        }
        if(check(k, nupx, ndwx, nlfy, nrgy, res-1, cnt)) {
        //    printf("%d 4\n",res);
            return 1;    
        }
        
        
        
        return 0;
    }
    
}
int read(){
    int x = 0; int f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*=f;
}


int main(){
    freopen("cover.in","r",stdin);
    freopen("cover.out","w",stdout);
    int n = read();
    int upx = -inf, dwx = inf, lfy = inf, rgy = -inf;
    
    for(int i = 1; i <= n; i++){
        p[i].x = read(), p[i].y = read(); tmp[i] = p[i];
        upx = max(upx, p[i].x), dwx = min(dwx, p[i].x);
        lfy = min(lfy, p[i].y), rgy = max(rgy, p[i].y);
    } 
    //sort(f + 1, f + 1 + n);
    int L = 1, R = max(upx - dwx, rgy - lfy), ans = -1;
    while(L <= R){
        for(int i = 1; i <= n; i++) tmp[i] = p[i];
        int mid = (L + R) >> 1;
        if(check(mid, upx, dwx, lfy, rgy, 3, n)) ans = mid, R = mid - 1;
        else L = mid + 1;
    }
    printf("%d\n", ans);
}
View Code

 

 

 

 

T3:

 

 我们发现第N个收费站是没有用的。。然后在操作时将r--简化操作。首先那个期望是很坑爹的。实际上,期望可以用总和/总状态数解决,我们不妨设a<b,这样期望不变(因为a≠b),从而总状态数即(r-l+2)*(r-l+1)/2。然后考虑总和,我们对每一个收费站单独分析。对于收费站i,它对总和的贡献为v[i]*(i-l+1)*(r-i+1),可以发现(i-l+1)*(r-i+1)即经过i的路径的条数。因此查询l,r的总和即Σ(i=l,r) v[i]*(i-l+1)*(r-i+1)。

 

       然后就变成了区间修改区间查询的经典线段树题目了(不过好像并没有什么转化)

 

       但是直接维护答案(或者用几个辅助数组)是会写疯掉的。。(←亲身体验)。我们把查询的那个式子展开,然后以i为主元降幂排列把那个式子化成:

 

       Σ(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]

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#define for2(a,b,i) for(int i=a;i>=b;--i)
#define for1(a,b,i) for(int i=a;i<=b;++i)
#define maxn 100005
using namespace std;  
typedef long long ll;
 
inline ll read(){
     char x=getchar();ll f=1,sum=0;
     while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
     while(x>='0'&&x<='9'){sum=sum*10+x-'0';x=getchar();}
     return f*sum;       
}
 
int n,m;
ll ans2,ans1,ans,he[maxn];
char kk[2];
 
inline ll gcd(ll x,ll y){
     if(!y) return x;
     return gcd(y,x%y);
}

ll sum2[maxn*4],sum1[maxn*4],sum[maxn*4],data[maxn*4];
inline void mainten(int root,int l,int r,ll zhi){
      data[root]+=zhi; sum2[root]+=(he[r]-he[l-1])*zhi;
      sum1[root]+=(ll)(l+r)*(r-l+1)/2*zhi;
      sum[root]+=(ll)(r-l+1)*zhi;   
}

inline void updata(int root,int l,int r){
      int mid=(l+r)>>1;
      mainten(root<<1,l,mid,data[root]);
      mainten(root<<1|1,mid+1,r,data[root]);
      data[root]=0;
}

inline void updata2(int root){
      sum2[root]=sum2[root<<1]+sum2[root<<1|1];
      sum1[root]=sum1[root<<1]+sum1[root<<1|1];
      sum[root]=sum[root<<1]+sum[root<<1|1];
}

inline void change(int lx,int rx,int root,int l,int r,ll zhi){
     if(lx<=l&&rx>=r){mainten(root,l,r,zhi);return;}
     int mid=(l+r)>>1;
     if(data[root]) updata(root,l,r);
     if(lx<=mid) change(lx,rx,root<<1,l,mid,zhi);
     if(rx>mid)  change(lx,rx,root<<1|1,mid+1,r,zhi);
     updata2(root);
}

inline void query(int lx,int rx,int root,int l,int r){
     if(lx<=l&&rx>=r){
         ans2+=sum2[root]; ans1+=sum1[root]; ans+=sum[root];
         return;
     }
     int mid=(l+r)>>1;
     if(data[root]) updata(root,l,r);
     if(lx<=mid)  query(lx,rx,root<<1,l,mid);
     if(rx>mid)   query(lx,rx,root<<1|1,mid+1,r);
     updata2(root);
}
 
inline void change(){
     ll x=read(),y=read(),z=read();
     change(x,y-1,1,1,n,z);
}
 
inline void query(){
     ll x=read(),y=read(); ll oo=0;
     ans2=ans1=ans=0; ll di=(ll)(y-x+1)*(y-x)>>1;
     query(x,y-1,1,1,n);
     oo=(ll)(y+x-1)*ans1+(ll)(-y*x+y)*ans-ans2;
     ll GCD=gcd(oo,di);
     printf("%lld/%lld\n",oo/GCD,di/GCD);
}
 
int main(){  
freopen("roadxw.in","r",stdin);
freopen("roadxw.out","w",stdout);
     n=read(),m=read();
     for1(1,n,i) he[i]=he[i-1]+(ll)i*i;
     while(m--){
         scanf("%s",&kk);
         if(kk[0]=='C') change();
         else           query();
     }
}
View Code

 

还用一种方法,用线段树维护区间的各种和,前缀和的和,后缀和的和,考虑线段树合并时每次跨过中间的贡献,是等差数列形式;

 

       然后我们就只需要维护i*i*a[i],i*a[i],a[i]的区间和了。线段树轻松胜任。最后输出的时候再转化一下就好了。

 

#include<cstring>
#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+5;
struct node{
    long long lsum,rsum,an,sum;
    long long fl;
}f[N*4];
long long b[N],c[N];
inline int read()
{
    int ans=0,t=1;
    char x=getchar();
    while(x<'0'||x>'9')
    {
        if(x=='-') t=-1;
        x=getchar();
    }
    while(x>='0'&&x<='9')
    {
        ans=ans*10+x-'0';
        x=getchar();
    }
    return ans*t;
}
node up(node ls,node rs,long long llen,long long rlen)
{
    node ans;
    ans.sum=ls.sum+rs.sum;
    ans.lsum=ls.lsum+rs.lsum+rlen*ls.sum;
    ans.rsum=rs.rsum+ls.rsum+llen*rs.sum;
    ans.an=ls.an+rs.an+ls.rsum*rlen+rs.lsum*llen;
    ans.fl=0;
    return ans;
}
void push_down(int o,int l,int r)
{
    if(f[o].fl!=0)
    {
        int ls=o<<1;
        int rs=o<<1|1;
        int mid=(r+l)>>1;
        long long llen=(mid-l+1);
        long long rlen=(r-mid);
        f[ls].sum+=llen*f[o].fl;
        f[rs].sum+=rlen*f[o].fl;
        f[ls].lsum+=(c[llen]*f[o].fl);
        f[ls].rsum+=(c[llen]*f[o].fl);
        f[rs].lsum+=(c[rlen]*f[o].fl);
        f[rs].rsum+=(c[rlen]*f[o].fl);        
        f[ls].an+=(b[llen]*f[o].fl);
        f[rs].an+=(b[rlen]*f[o].fl);
        f[ls].fl+=f[o].fl;
        f[rs].fl+=f[o].fl;
        f[o].fl=0;
    }
}
void modi(int o,int l,int r,const int L,const int R,long long data)
{
    if(L<=l&&R>=r)
    {
        long long len=(r-l+1);
        f[o].rsum+=(c[len]*data);
        f[o].lsum+=(c[len]*data);
        f[o].sum+=data*len;
        f[o].an+=(b[len]*data);
        f[o].fl+=data;
        return ;
    }
    int mid=(l+r)>>1;
    int ls=o<<1;
    int rs=o<<1|1;
    push_down(o,l,r);
    if(L<=mid) modi(ls,l,mid,L,R,data);
    if(R>mid) modi(rs,mid+1,r,L,R,data);
    f[o]=up(f[ls],f[rs],mid-l+1,r-mid);
}
node query(int o,int l,int r,int L,int R)
{
    if(L<=l&&R>=r)
    {
        return f[o];
    }
    int mid=(l+r)>>1;
    push_down(o,l,r);
    int ls=o<<1;
    int rs=o<<1|1;
    if(L>mid) return query(rs,mid+1,r,L,R);
    if(R<=mid) return query(ls,l,mid,L,R);
    node lans=query(ls,l,mid,L,mid);
    node rans=query(rs,mid+1,r,mid+1,R);
    long long llen=mid-L+1,rlen=R-mid;
    node ans;
    ans.sum=lans.sum+rans.sum;
    ans.lsum=lans.lsum+rans.lsum+rlen*lans.sum;
    ans.rsum=rans.rsum+lans.rsum+llen*rans.sum;
    ans.an=lans.an+rans.an+lans.rsum*rlen+rans.lsum*llen;
    ans.fl=0;
    return ans;    
}
inline long long gcd(long long a,long long b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}
char s1[5];
int main()
{
    freopen("roadxw.in","r",stdin);
    freopen("roadxw.out","w",stdout);
    int n,m;
    n=read();
    m=read();
    for(long long i=1;i<=n;i++) b[i]=i*(i+1)/2;
    for(int i=1;i<=n;i++) b[i]+=b[i-1];
    for(long long i=1;i<=n;i++) c[i]=i*(i+1)/2;
    int len=n-1;
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s1);
        if(s1[0]=='C'){
            int x,y,z;
            x=read();
            y=read();
            y--;
            z=read();
            modi(1,1,len,x,y,z);
        }
        if(s1[0]=='Q')
        {
            int x,y;
            x=read();
            y=read();
            y--;
            long long mu=1ll*(y-x+1)*(y-x+2)/2;
            node ans=query(1,1,len,x,y);
            long long zi=ans.an;
            long long g=gcd(mu,zi);
            printf("%lld/%lld\n",zi/g,mu/g);
           }
    }
    return 0;
}
View Code

 

 

 

posted @ 2018-11-05 19:07  Ed_Sheeran  阅读(87)  评论(0编辑  收藏