zoukankan      html  css  js  c++  java
  • HDU 6155 Subsequence Count(矩阵乘法+线段树+DP)

    题意

    给定一个长度为 (n)(01) 串,完成 (m) 种操作——操作分两种翻转 ([l,r]) 区间中的元素、求区间 ([l,r]) 有多少个不同的子序列。

    (1 leq n,m leq 10^5)

    思路

    看到这种题目,应该条件反射的去想一下线段树。

    但首先还是从一个询问开始,对于一个长度为 (n) 的串,设 (dp_{i,j}) 为前 (i) 位组成的序列中,以 (j) 结尾的串的个数,若串的第 (i) 位为 (j) 有递推式:

    (dp_{i,j}=dp_{i-1,0}+dp_{i-1,1}+1)

    (dp_{i,!j}=dp_{i-1,!j})

    上式是以 (0j,1j) 结尾的串的个数,加上单独一个(j) ;下式则直接转移上一位的信息。

    那么将 ({dp_{0,0},dp_{0,1},1}) 作为初始矩阵,用线段树维护区间对应的转移矩阵即可。

    代码

    #include<bits/stdc++.h>
    #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
    #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
    typedef long long LL;
    using namespace std;
    const int N=1e5+5;
    const int P=1e9+7;
    struct Matrix
    {
    	int n,m,a[4][4];
    	int *operator [](const int x){return a[x];}
    	void resize(int _n,int _m){n=_n,m=_m;}
    	Matrix operator *(const Matrix &_)const
    	{
    		Matrix res;res.resize(n,_.m);
    		FOR(i,1,n)FOR(j,1,_.m)
    		{
    			res[i][j]=0;
    			FOR(k,1,m)(res[i][j]+=1ll*a[i][k]*_.a[k][j]%P)%=P;
    		}
    		return res;
    	}
    	void flip()
    	{
    		swap(a[1][1],a[2][2]);
    		swap(a[1][2],a[2][1]);
    		swap(a[3][1],a[3][2]);
    	}
    	Matrix operator *=(const Matrix &_){return (*this)=(*this)*_;}
    };
    const Matrix Zero=(Matrix){
    	3,3,
    	0,0,0,0,
    	0,1,0,0,
    	0,1,1,0,
    	0,1,0,1};
    const Matrix One =(Matrix){
    	3,3,
    	0,0,0,0,
    	0,1,1,0,
    	0,0,1,0,
    	0,0,1,1};
    Matrix nd[N<<2],A;
    int tag[N<<2];
    char str[N];
    
    void build(int k,int l,int r)
    {
    	tag[k]=0;
    	if(l==r)
    	{
    		if(str[l]=='0')nd[k]=Zero;
    		else nd[k]=One;
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(k<<1,l,mid);
    	build(k<<1|1,mid+1,r);
    	nd[k]=nd[k<<1]*nd[k<<1|1];
    }
    void push_down(int k)
    {
    	if(!tag[k])return;
    	tag[k<<1]^=1,nd[k<<1].flip();
    	tag[k<<1|1]^=1,nd[k<<1|1].flip();
    	tag[k]=0;
    }
    void update(int k,int L,int R,int l,int r)
    {
    	if(L<=l&&r<=R)
    	{
    		tag[k]^=1,nd[k].flip();
    		return;
    	}
    	push_down(k);
    	int mid=(l+r)>>1;
    	if(L<=mid)update(k<<1,L,R,l,mid);
    	if(R>mid)update(k<<1|1,L,R,mid+1,r);
    	nd[k]=nd[k<<1]*nd[k<<1|1];
    }
    Matrix query(int k,int L,int R,int l,int r)
    {
    	if(L<=l&&r<=R)return nd[k];
    	push_down(k);
    	int mid=(l+r)>>1;
    	if(R<=mid)return query(k<<1,L,R,l,mid);
    	else if(L>mid)return query(k<<1|1,L,R,mid+1,r);
    	else return query(k<<1,L,R,l,mid)*query(k<<1|1,L,R,mid+1,r);
    }
    
    int main()
    {
    	A.resize(1,3);
    	A[1][1]=0,A[1][2]=0,A[1][3]=1;
    	int T,n,Q;
    	scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d%d",&n,&Q);
    		scanf("%s",str+1);
    		build(1,1,n);
    		int op,x,y;
    		while(Q--)
    		{
    			scanf("%d%d%d",&op,&x,&y);
    			if(op==1)update(1,x,y,1,n);
    			else
    			{
    				Matrix res=A*query(1,x,y,1,n);
    				printf("%d
    ",(res[1][1]+res[1][2])%P);
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    623. Add One Row to Tree 将一行添加到树中
    771. Jewels and Stones 珠宝和石头
    216. Combination Sum III 组合总数三
    384. Shuffle an Array 随机播放一个数组
    382. Linked List Random Node 链接列表随机节点
    向github项目push代码后,Jenkins实现其自动构建
    centos下安装Jenkins
    python提取批量文件内的指定内容
    批处理实现:批量为文件添加注释
    python抓取每期双色球中奖号码,用于分析
  • 原文地址:https://www.cnblogs.com/Paulliant/p/10189068.html
Copyright © 2011-2022 走看看