bzoj 4664: Count

这道题和bzoj上一道叫魔法碰撞的题很像,只不过做法更加巧妙了。

一开始的想法是$f[i][j][k][0/1/2]$表示后i个数有j段当前混乱程度为k的方案,最后一维表示边界还能放几个。

转移的时候枚举每个数是山峰山谷或者中间的数,然后让混乱程度加上$h$或减去$h$.

但是这样做第三维的状态数太大了。

转变思路,从小到大枚举i,让第三维的意义变为当前每一段中的数两两之间的混乱度加上之后合并完所有段后至少会产生的混乱度。

每次$k=k+(j*2+l-2)*(h[i+1]-h[i])$,就是现在每个空的代价会加上$(h[i+1]-h[i])$,现在放$h[i+1]$的时候不会产生新的代价。

这样第三维就是递增的了,最大就是L。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 105
using namespace std;
const int p = 1000000007;
int n,L;
int h[N];
int f[2][105][1205][3];
int main()
{
//  freopen("count.in","r",stdin);
//  freopen("count.out","w",stdout);
    scanf("%d%d",&n,&L);
    for(int i=1;i<=n;i++)scanf("%d",&h[i]);
    sort(h+1,h+n+1);
    if(h[n]-h[1]>L)
    {
        puts("0");return 0;
    }
    if(n==1)
    {
        puts("1");
        return 0;
    }
    // f[i][j][k][0] 前i个数被分成了j段混乱度是k
    int now=0,pre=1;
    f[0][1][0][1]=2;
    f[0][1][0][2]=1;
    for(int i=1;i<n;i++)
    {
        now^=1;pre^=1;
        memset(f[now],0,sizeof(f[now]));
        // 处理i+1
        for(int j=1;j<=i;j++)
        {
            for(int k=0;k<=L;k++)
            {
                for(int l=0;l<=2;l++)
                {
                    if(f[pre][j][k][l])
                    {
                        int tmp=f[pre][j][k][l];
                        int tp=k+(h[i+1]-h[i])*(2*j+l-2);
                        if(tp>L)continue;
                        if(l)
                        {
                            (f[now][j][tp][l-1]+=1LL*tmp*l%p)%=p;
                            (f[now][j+1][tp][l-1]+=1LL*tmp*l%p)%=p;
                        }
                        (f[now][j-1][tp][l]+=1LL*tmp*(j-1)%p)%=p;
                        (f[now][j+1][tp][l]+=1LL*tmp*(j-1+l)%p)%=p;
                        (f[now][j][tp][l]+=1LL*tmp*(2*j-2+l)%p)%=p;
                    }
                }
            }
        }
    }
    int ans=0;
    for(int i=0;i<=L;i++)
    {
        ans+=f[now][1][i][0];
        ans%=p;
    }
    printf("%d\n",ans);
    return 0;
}

  

时间: 2024-11-09 14:34:57

bzoj 4664: Count的相关文章

bzoj 3956: Count

3956: Count Description Input Output Sample Input 3 2 0 2 1 2 1 1 1 3 Sample Output 0 3 HINT M,N<=3*10^5,Ai<=10^9 Source CH Round#64 MFOI杯水题欢乐赛day1 By Gromah 题解: 性质很妙的一道题. 首先可以发现这个所有的点队数是很小的,考虑以把一个点作为两个端点中较小的一个,那么另一个端点肯定是向左向右第一个大于等于它的点,这是很显然的.... 我们

BZOJ 2588 Count on a tree 主席树+倍增LCA

题目大意:给定一棵树,每个节点有权值,询问两个节点路径上的权值第k小 这题很卡时间... 树链剖分+二分+树套树的O(nlog^4n)做法可以去死了 没有修改操作,树链剖分+二分+划分树O(nlog^3n),还是死了 我怒了,裸学了一发可持久化线段树(不看任何代码OTZ,我是怎么做到的0.0),二分+主席树,O(nlog^2n),居然还是死了! 最后发现我SB了,完全没有必要二分,直接把4个参全传下去就行了,O(nlogn) 首先我们对于每个节点维护这个节点到根的权值线段树 然后对于每个询问(x

bzoj 2588 Count on a tree 解题报告

Count on a tree 题目描述 给定一棵\(N\)个节点的树,每个点有一个权值,对于\(M\)个询问\((u,v,k)\),你需要回答\(u\) \(xor\) \(lastans\)和\(v\)这两个节点间第\(K\)小的点权.其中\(lastans\)是上一个询问的答案,初始为\(0\),即第一个询问的u是明文. 输入输出格式 输入格式: 第一行两个整数\(N,M\). 第二行有\(N\)个整数,其中第\(i\)个整数表示点\(i\)的权值. 后面\(N-1\)行每行两个整数\((

bzoj 2588 Count on a tree

Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v,k),表示一组询问. Output M行,表示每个询问的答案.最后一个询问不输出换行符 S

BZOJ 2588 Count on a tree (COT) 是持久的段树

标题效果:两棵树之间的首次查询k大点的权利. 思维:树木覆盖树,事实上,它是正常的树木覆盖了持久段树. 由于使用权值段树可以寻求区间k大,然后应用到持久段树思想,间隔可以做减法.详见代码. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define NIL (tree[0]) using names

BZOJ 2588 Count on a tree (COT) 可持久化线段树

题目大意:查询树上两点之间的第k大的点权. 思路:树套树,其实是正常的树套一个可持久化线段树.因为利用权值线段树可以求区间第k大,然后再应用可持久化线段树的思想,可以做到区间减法.详见代码. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define NIL (tree[0]) using name

BZOJ 1452 Count

长知识啦..二维BIT. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,q,t[105][305][305],map[305][305]; int a,b,c,d,e,f; int lowbit(int x) { return x&(-x); } void update(int x,int y

[bzoj] 1036 Count

原题 树链剖分板子题 树剖详解: #include<cstdio> #include<algorithm> typedef long long ll; #define N 30010 using namespace std; int n,x,y,m,a[N],f[N],dfn[N],deep[N],head[N],cnt=1,tp[N],ref[N],t,son[N],size[N]; char s[10]; struct hhh { int to,next; }edge[2*N]

二维数点 bzoj 3956 Count

https://www.lydsy.com/JudgeOnline/problem.php?id=3956 70分好像可以莫队 首先要发现答案是\(\mathcal O(n)\)的 Proof: 考虑一个点\(a_k\)对区间\([l,r]\)的贡献 当且仅当\(a_k=max(a_{l+1}, ... , a_{r-1})\)且\(a_l>a_k\), \(a_k<a_r\) 于是对于一个点预处理一下\((x, y)\)左边比他大的\(x\), 右边比他大的\(y\) 重复的数字的话 答案就