hdu 5068 线段树加+dp

这题说的是 有n 层每层 有两个门 每个门 可以到达上一层的两个门,然后求从a 层到达b 层的方案总数, 不能后退, 在同一层中不能从第一个门到达另一层

我们只要我们可以对于每个 区间内 有dp[o][2][2] , 表示 在这个区间中 从区间起始到达区间末尾 的两个门分别设 a1,a2, b1,b2, dp[o][0][0],和dp[o][0][1],表示从从a1到b1 和 a2 到 b1 的方案总数 然后同理dp[o][1][0]dp[o][1][1],

得到转移 通过线段树去优化他 得到转移 一旦ij 两地相通那么显然 i这个点 的 在前面这个区间的a1 a2 相应的乘上 后面 这个区间在 j 这个位置开始的方案总数, 得到他们在总区间结束时的在 b1上有多少个方案从前面区间的 a1 和a2 过来,通过这样得到整个区间的值

        for(int i=0; i<2; ++i)
            for(int j=0; j<2; ++j)
                if(star[mid][i][j]){
                  value[o].M[0][0]=(value[o].M[0][0] + value[o*2].M[i][0] * value[o*2+1].M[0][j] % mod )%mod;
                   value[o].M[0][1]=(value[o].M[0][1] + value[o*2].M[i][1] * value[o*2+1].M[0][j]%mod )%mod;
                   value[o].M[1][0]=(value[o].M[1][0] + value[o*2].M[i][0] * value[o*2+1].M[1][j]%mod )%mod;
                   value[o].M[1][1]=(value[o].M[1][1] + value[o*2].M[i][1] * value[o*2+1].M[1][j]%mod )%mod;
                }

#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
typedef __int64 ll;
const int maxn = 50005;
const ll mod = 1000000007;
int loc,x,y;
struct Matx{
   ll M[2][2];
}P;
bool star[maxn+5][2][2];
void maintain(int mid ,Matx &A,Matx B){
         Matx ans;
         memset(ans.M,0,sizeof(ans.M));
           for(int i=0; i<2; ++i)
            for(int j=0; j<2; ++j)
                if(star[mid][i][j]){
                   ans.M[0][0]=(ans.M[0][0] + A.M[i][0] * B.M[0][j] )%mod;
                   ans.M[0][1]=(ans.M[0][1] + A.M[i][1] * B.M[0][j] )%mod;
                   ans.M[1][0]=(ans.M[1][0] + A.M[i][0] * B.M[1][j] )%mod;
                   ans.M[1][1]=(ans.M[1][1] + A.M[i][1] * B.M[1][j] )%mod;
                }
          for(int i=0; i<2; ++i)
            for(int j=0; j<2; ++j)
              A.M[i][j]=ans.M[i][j];
}
struct Itree{
    Matx value[maxn*4];

    void tain(int o,int mid){
         memset(value[o].M,0,sizeof(value[o].M));
           for(int i=0; i<2; ++i)
            for(int j=0; j<2; ++j)
                if(star[mid][i][j]){
                  value[o].M[0][0]=(value[o].M[0][0] + value[o*2].M[i][0] * value[o*2+1].M[0][j] % mod )%mod;
                   value[o].M[0][1]=(value[o].M[0][1] + value[o*2].M[i][1] * value[o*2+1].M[0][j]%mod )%mod;
                   value[o].M[1][0]=(value[o].M[1][0] + value[o*2].M[i][0] * value[o*2+1].M[1][j]%mod )%mod;
                   value[o].M[1][1]=(value[o].M[1][1] + value[o*2].M[i][1] * value[o*2+1].M[1][j]%mod )%mod;
                }
    }
    void build(int o, int L, int R){
         if(L==R){
           value[o].M[0][0]=1;
           value[o].M[0][1]=0;
           value[o].M[1][0]=0;
           value[o].M[1][1]=1;
             for(int i=0; i<2; ++i)
                for(int j=0; j<2;  ++j)
                  star[L][i][j]=true;
             return ;
         }
         int mid= (L+R)/2;
         build( o*2 , L , mid );
         build( o*2+1 , mid+1 , R );
         tain(o,mid);
    }

    void update(int o, int L, int R){
            if(L==R){
                star[L][x][y]=star[L][x][y]==false;
                return ;
            }
            int mid=(L+R)/2;
            if(loc<=mid) update(o*2,L,mid);
            else update(o*2+1,mid+1,R);
            tain(o, mid);
    }
    void query(int o, int L, int R){
            if( (L>=x&&R<=y) ){
                if(loc==0){
                    P=value[o];loc=1;
                }else{
                    maintain(L-1,P,value[o]);
                }
                return ;
            }
            int mid = (L+R)/2;
            if(x<=mid)
                query(o*2,L,mid);
            if(y>mid)
                query(o*2+1,mid+1,R);
    }
}T;
int main()
{
     int n,m;
     while(scanf("%d%d",&n,&m)==2){
        T.build(1,1,n);
        for(int i=0; i<m; ++i){
             loc=0;
             int op;
             scanf("%d",&op);
             if(op==0) {
                    scanf("%d%d",&x,&y);
                    T.query(1,1,n);
                    ll ans=0;
                    for(int i=0; i<2; ++i)
                         for(int j=0; j<2; ++j)
                          ans=(ans+ P.M[i][j])%mod;
                   printf("%I64d\n",ans);
             }else{
                 scanf("%d%d%d",&loc,&x,&y);
                 x--; y--;
                 T.update(1,1,n);
             }
        }
     }
    return 0;
}

时间: 2024-10-11 07:40:14

hdu 5068 线段树加+dp的相关文章

hdu 5068(线段树+矩阵乘法)

矩阵乘法来进行所有路径的运算, 线段树来查询修改. 关键还是矩阵乘法的结合律. Harry And Math Teacher Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 326    Accepted Submission(s): 89 Problem Description As we all know, Harry Porter

Vijos 小白逛公园 线段树加DP

描述 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了. 一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择**连续**的一些公园玩.小白当然希望选出的公园的分数总和尽量高咯.同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化. 那么,就请你来帮小白选择公园吧

HDU 3016 Man Down 线段树+简单DP

囧,一开始看错题意,后来才发现人是垂直下落的,被附带链接里的Man Down游戏误导了. 那就变成了一个简单的DAG模型动态规划,随意搞就ok了 #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include <string> #include <queu

【POJ 2750】 Potted Flower(线段树套dp)

[POJ 2750] Potted Flower(线段树套dp) Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4566   Accepted: 1739 Description The little cat takes over the management of a new park. There is a large circular statue in the center of the park, surrou

hdu 3308 线段树单点更新 区间合并

http://acm.hdu.edu.cn/showproblem.php?pid=3308 学到两点: 1.以区间端点为开始/结束的最长......似乎在Dp也常用这种思想 2.分类的时候,明确标准逐层分类,思维格式: 条件一成立: { 条件二成立: { } else { } } else { 条件二成立: { } else { } } 上面的这种方式很清晰,如果直接想到那种情况iif(条件一 &条件二)就写,很容易出错而且把自己搞乱,或者情况不全,,,我就因为这WA了几次 3.WA了之后 ,

HDU 4893 线段树裸题

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2512    Accepted Submission(s): 751 Problem Description Recently, Doge got a funny birthday present from his new friend, Pro

HDU 4902 线段树(区间更新)

Nice boat Time Limit: 30000/15000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 353    Accepted Submission(s): 169 Problem Description There is an old country and the king fell in love with a devil. The devil alw

hdu 4893 线段树 --- 也是两个变 类似双标记

http://acm.hdu.edu.cn/showproblem.php?pid=4893 开始的时候,我按双标记,WA了一下午,搞不定,我是用的两个标记add--表示当前结点中有值发生变化,flag,斐波那契的懒惰标记,但是估计是我自己处理的有问题,一直不对 参考了别人的代码,写法还是很不错的,Add变量维护的是,完全变成Fibonacci的时候的和,---回头我再重新写一遍 #include <cstdio> #include <cstring> #include <a

HDU 4902 线段树||暴力

给定一个序列,两种操作 1:把一段变成x. 2:把一段每个数字,如果他大于x,就变成他和x的gcd,求变换完后,最后的序列. 线段树解法:用lazy标记下即可,优化方法还是很巧妙的, Accepted 4902 515MS 3308K 1941 B C++ #include "stdio.h" #include "string.h" struct node { int l,r,x;// 在叶子节点代表值,树节点代表成端更新的lazy操作. }data[400010]