zoukankan      html  css  js  c++  java
  • 8.8爆0祭

    T3:

    ... ...不会

    选k个集合,每个集合的价值就是线段交集的长度,求最大总价值

    不存在两个集合的价值都为空

    因为我们可以把一个空集里面的线段只留下1条,其他的放在另一个空集里面

    只有一个空集:把前k-1长的线段拿出来(取)

    没有空集:

    一个性质:

    如果有一条线段包含另一条线段,则他们放在同一个集合里面,不对答案有影响

    所以只考虑不被任何一条线段包含的线段

    按照左端点排序,然后右端点也是递增的(因为只考虑哪些不包含别的线段的线段,如果右端点不递增,就说明里面有包含别的线段的线段)

    然后就可以dp了???

    一个集合里的线段应该是连续的一段线段构成的

    dp[i][j]:前i条线段分成j组的最大贡献(集合里的线段是连续的)

    dp[i][j]=max(dp[x][j-1]+r[x+1]-l[i])(r[x+1]>l[i])

    可以单调队列优化......

     stdの神仙代码:

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<functional>
    using namespace std;
    const int N=6006;
    typedef long long ll;
    const ll inf=5e13;
    
    int n,m,k,len_n,r;
    
    ll len[N],ans,dp[2][N];
    struct aa
    {
        int L,R;
    }t1[N],t2[N];
    bool cmp(aa a,aa b) 
    {
        if (a.R==b.R) return a.L>b.L;
        return a.R<b.R;
    }
    
    int q[N],H,T;
    
    
    int main()
    {
        
        
        freopen("C.in","r",stdin);
        freopen("C.out","w",stdout);
        scanf("%d%d",&n,&k);
        for (int i=1;i<=n;i++) scanf("%d%d",&t1[i].L,&t1[i].R),len[i]=t1[i].R-t1[i].L;
        
        sort(len+1,len+n+1);
        for (int i=0;i<k-1;i++) ans+=len[n-i];
        
        sort(t1+1,t1+n+1,cmp);
        
        
        memset(len,0,sizeof(len));
        int mx=0;
        for (int i=1;i<=n;i++)
        if (t1[i].L>mx)
        {
            t2[++m]=t1[i];
            mx=t1[i].L;
        }else len[++len_n]=t1[i].R-t1[i].L;//计算被其他线段包含的小线段的长度
        
        sort(len+1,len+len_n+1,greater<ll>());
        for (int i=2;i<=n;i++) len[i]+=len[i-1];
        
        sort(t2+1,t2+m+1,cmp);//对不包含别的线段的大线段进行排序
        
        r=1;
        
        for (int i=1;i<=m;i++) 
        if (t2[1].R<=t2[i].L) dp[0][i]=-inf;else dp[0][i]=t2[1].R-t2[i].L;//如果当前点和1号点隔得太远那初始值就是不合法
        
        ans=max(ans,dp[0][m]+len[k-1]);//考虑只有一个空集的情况
        
        for (int j=2;j<=min(k,m);j++,r^=1) 
        {
            q[H=T=1]=1;dp[r][1]=-inf;//q数组是手写的队列,dp数组用了玄学的滚动数组优化qwq
            for (int i=2;i<=m;i++)
            {
                while (H<=T&&t2[q[H]+1].R<=t2[i].L) H++;//当队首+1的右端点要比当前枚举的i的左端点小的时候就弹掉,相当于保证上面的r[x+1]>l[i]
                if (H<=T) dp[r][i]=dp[r^1][q[H]]+t2[q[H]+1].R-t2[i].L;else dp[r][i]=-inf;
                while (H<=T&&dp[r^1][i]+t2[i+1].R>=dp[r^1][q[T]]+t2[q[T]+1].R)  T--;
                q[++T]=i;
            }
            ans=max(ans,dp[r][m]+len[k-j]);//len[k-j]表示第j+1条小线段到第k条小线段的长度和
        }
        
        printf("%lld
    ",ans);
        return 0;
    }

    (至于dp的那两维枚举的啥我是不知道了)(逃

    精妙的T1

    打表???

    一个奇怪的哈夫曼树????

    啥是哈夫曼树啊

    能吃吗?

    不能吧.....

    既然说到哈夫曼树了,就先来一棵树

    树上任何一条路径代表一个字符串

    如果是前缀,就说明一个点是另一个的结尾的祖先

    那字符串的数量就是叶子的数量,长度就是叶子到根的路径的长度

    显然越深的路径给出现次数越少的(这货是排序不等式???)

    任意一个n个叶子的数对应一个答案

    显然一个节点有两个孩子比只有一个孩子要优

    我们可以从根开始找孩子

    设计构造树的dp(左子树深度+2,右子树深度+1)

    我们可以让某一个节点不让它继续长孩子,然后这就是一个字符串了

    dp[i][x][y][k]:设到了第i层,上一层的芽(可以继续长孩子的节点)有x个,这一层有y个节点,i层之前有k个叶子

    i:要知道深度,所以要记录深度

    长叶子的时候一根是+2的,一根是+1的,所以要记录两层(x,y))

    枚举i层有多少芽成了叶子

    如果有p个叶子,则代价为i*(sum[k+p]-sum[k]),sum[i]为前i个的出现次数)

    dp[i][x][y][k]=max{i*(sum[k+p]-sum[k])+dp[i+1][y+x-p][x-p][k+p]}

     dp[i+1][y+x-p][x-p][k+p]就是长了p个叶子之后的状态

    why???

    因为当前只考虑i-1层

    蛤?为什么第三维没有y?

    窝也不造

    复杂度:O(n5)

    妈耶我炸了

    考虑每层的贡献

    第一层:sum*n

    第i层的贡献就是第i层之后的叶子的数量

    既然搞出来了贡献,那是不是就不用记录深度了呢?

    所以第一维没了

    变成了dp[x][y][k](x是当前的芽,y是下一层的芽,k是之前的叶子)

    dp[x][y][k]=min{dp[x+y-p][x-p][k+p]+sum[n]-sum[k+p]}

    长出了p个叶子,剩下了sum[n]-sum[k+p]的叶子,剩下的每个叶子都要经过第i层,产生的代价就是sum[n]-sum[k+p]

    复杂度O(n4)

    还能优化吗?

    考虑这些维和p

    emmmmm还是去搞p吧,维度搞掉就逗了

    可以每次让y-1,当我不想长叶子的时候就全部是芽,

    转移过后:

    ①:长一个叶子:dp[x-1][y][k+1](就是让一个芽变成了叶子,不往下走)

    ②:我不想长叶子,继续往下长:dp[x+y][x][k]+sum[n]-sum[k](因为往下走了一层,所以要统计对答案的贡献)

    stdの神仙code(中日英三文)

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<cassert>
    using namespace std;
    const int N=755;
    typedef unsigned int uint;
    const uint inf=(1u<<31)-1;
    
    
    uint n,a[N],sum[N];
    
    bool cmp(uint x,uint y) {return x>y;}
    
    uint dp[2][N][N];int r;
    
    int main()
    {
        
        freopen("A.in","r",stdin);
        freopen("A.out","w",stdout);
        
        
        scanf("%u",&n);
        //assert(3<=n && n<=750);
        
        for (int i=1;i<=n;i++) scanf("%u",&a[i]),assert(1<=a[i] && a[i]<=100000);
        
        sort(a+1,a+n+1,cmp);
        for (int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
        
        for (int x=0;x<N;x++)
            for (int y=0;y<N;y++) dp[0][x][y]=dp[1][x][y]=inf;//dp数组神滚动优化
        
        
        dp[r=0][0][0]=0;
        for (int k=n-1;k>=0;k--) //枚举还剩下多少个叶子没有长
        {
            for (int y=n-k;y>=0;y--)//上文的x
                for (int x=n-k-y;x>=0;x--)//上文的y 
                dp[r^1][x][y]=
                min(inf,x==0&&y==0?inf:
                    min(
                    y==0?inf:dp[r][x][y-1],
                    x+y+y+k>n?inf:sum[n]-sum[k] + dp[r^1][y][x+y]
                    ) 
                );
            
        /*    for (int x=0;x<=n-(k+1);x++)
                for (int y=0;y<=n-(k+1)-x;y++) dp[r][x][y]=inf;*/
            r^=1;
        }
        
        
        printf("%u
    ",sum[n]+dp[r][1][1]);
        return 0;
    }

     以及充(wo)满(bu)套(hui)路(da)的T2

    这里我们不确定的就是最终的各位数字之和

    所以我们要枚举和!!!

    边界条件:now==0&&k(%枚举的和的余数)==0&&当前各个数位之和为枚举的和:返回1,now==0但不满足上述条件返回0

    代代代代码(from std)

    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    
    ll A,B;
    int C;
    int a[70],na,b[70],nb,p[70];
    
    int mod;
    ll f[70][400][400];
    
    ll query(int *a,int i,int sum,int rest,bool o) //放到a数组里,i是填到第几位,sum为当前枚举的和-当前数字各个位数之和,rest为各位之和%当前枚举的和,o是是否顶上界
    {
        if (sum<0) return 0;
        if (i==0) return sum==0&&rest==0 ? 1:0;
         
        if (!o&&f[i][sum][rest]!=-1) return f[i][sum][rest];
        ll ans=0;
        int up=o ? a[i] : C-1;
         
        for (int j=0;j<=up;j++) 
        ans+=query(a,i-1,sum-j,(rest+mod-j*p[i]%mod)%mod,o&&j==up);
        if (!o) f[i][sum][rest]=ans;
        return ans;
    }
    
    int main()
    {
        freopen("B.in","r",stdin);
        freopen("B.out","w",stdout);
        scanf("%lld%lld%lld",&B,&A,&C);
        
        B--;
        
        while (A) a[++na]=A%C,A/=C;
        while (B) b[++nb]=B%C,B/=C;
         
         //printf("na=%d
    ",na);
        memset(f,-1,sizeof(f));
        
        ll ans=0;
        for (int i=1;i<=na*C;i++)
        {
            mod=i;
            p[1]=1;
            for (int j=2;j<=na;j++) p[j]=p[j-1]*C%mod;
             
            ans+=query(a,na,mod,0,true)-(nb==0 ? 0:query(b,nb,mod,0,true));
             
            for (int x=0;x<=na;x++)
                for (int y=0;y<=i;y++) 
                    for (int z=0;z<=mod;z++) f[x][y][z]=-1;
        }
        printf("%lld",ans);
        return 0;
    }
    std の code
  • 相关阅读:
    Datediff的使用(统计本日,昨日,本周,本月)
    数据库之查询语句
    数据库之表关系
    数据库中的数据类型
    数据库概念和基本操作
    IO模型
    并发编程之协程
    GIL与多线程(线程池与进程池,同步异步阻塞非阻塞)
    并发编程之多线程
    并发编程之多进程
  • 原文地址:https://www.cnblogs.com/lcez56jsy/p/11323867.html
Copyright © 2011-2022 走看看