zoukankan      html  css  js  c++  java
  • 平时二十四测

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

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

    题解:

    思路很棒的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
    ",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
    ",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
    ",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
    ",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
    ",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
    ", 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
    ",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
    ",zi/g,mu/g);
               }
        }
        return 0;
    }
    View Code

     

  • 相关阅读:
    JPEG compression
    GPU编程库
    亚马逊AWS服务器CentOS/Linux系统Shell安装Nginx及配置自启动
    如何查看mac系统是32位还是64位的操作系统
    mac解压7z格式文件
    vi中跳到文件的第一行和最后一行
    docker学习记录
    git 比较两个分支不同的commit
    meta property=og标签含义及作用
    php发现一个神奇的函数
  • 原文地址:https://www.cnblogs.com/EdSheeran/p/9910861.html
Copyright © 2011-2022 走看看