Codeforces 1380 F. Strange Addition —— 线段树优化矩阵快速幂

This way

题意:

现在有一种加法,假设上面是1,下面是b,那么他们相加得到的c是这个样子的:
在这里插入图片描述
现在给你一个数组c,并且有多次修改每个位置的数字是什么,问你每次修改完之后,问你a+b=c有多少种a,b数组

题解:

假设这个数组是a,设b[i]=a[i-1]*10+a[i]
设v[i]表示当前值为i的时候,有多少种加法方式
那么可以写出dp[i]=dp[i+1]*v[a[i]]+dp[i-2]*v[b[i]]
但是有修改,所以不能直接做,由于是单点修改,那么此时我们可以将其放到线段树当中,在上传时重载*号,计算答案即可。
可以发现是这样一个矩阵
[ v [ a [ i ] ] v [ b [ i ] ] 1 0 ] [ f [ i − 1 ] f [ i − 2 ] ] \left[ \begin{matrix} v[a[i]] & v[b[i]] \\ 1 & 0 \end{matrix} \right] \left[ \begin{matrix} f[i-1] \\ f[i-2] \end{matrix} \right] [v[a[i]]1v[b[i]]0][f[i1]f[i2]]
那么我们线段树优化的时候,只需要将左边那个矩阵当中的乘起来就行了,注意需要从右往左去做。
代码是队友敲得啦,思想是这么个思想

//#pragma GCC optimize(3)
//#include<ext/pb_ds/assoc_container.hpp>
//#include<ext/pb_ds/hash_policy.hpp>
#include<bits/stdc++.h>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
#define pb push_back
#define eb emplace_back
#define mst(a,b) memset(a,b,sizeof(a))
using namespace std;
//using namespace __gnu_pbds;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-12;
const double PI=acos(-1.0);
const double angcst=PI/180.0;
const ll mod=998244353;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}

struct rect
{
    ll a,b,c,d;
    rect(){}
    rect(ll a,ll b,ll c,ll d):a(a),b(b),c(c),d(d){}
    rect operator * (const rect &r) const
    {
        return rect(
            (a*r.a+b*r.c)%mod,(a*r.b+b*r.d)%mod
            ,(c*r.a+d*r.c)%mod,(c*r.b+d*r.d)%mod
        );
    }
};

char str[500050];
int a[500050],b[500050];
int v[666];
ll dp[500050];

rect tree[500050<<2];

void push_up(int p)
{
    tree[p]=tree[p<<1|1]*tree[p<<1];
}
void buildTree(int p,int l,int r)
{
    if(l==r)
    {
        tree[p]=rect(v[a[l]],a[l-1]==1?v[b[l]]:0,1,0);
        return;
    }
    int mid=(l+r)>>1;
    buildTree(p<<1|1,mid+1,r);
    buildTree(p<<1,l,mid);
    push_up(p);
}

void update(int p,int l,int r,int pos)
{
    if(l==r)
    {
        tree[p]=rect(v[a[l]],a[l-1]==1?v[b[l]]:0,1,0);
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)
        update(p<<1,l,mid,pos);
    else
        update(p<<1|1,mid+1,r,pos);
    push_up(p);
}

void solve()
{
    int n,m;
    cin>>n>>m;
    cin>>str+1;
    
    rep(i,1,n)
        a[i]=str[i]-'0';
    rep(i,2,n)
        b[i]=a[i-1]*10+a[i];
    rep(i,0,9)
        v[i]=i+1;
    rep(i,10,18)
        v[i]=19-i;
    
    buildTree(1,1,n);
    
    while(m--)
    {
        int p,v;
        cin>>p>>v;
        a[p]=v;
        b[p]=a[p-1]*10+a[p];
        b[p+1]=a[p]*10+a[p+1];
        update(1,1,n,p);
        if(p!=n)
            update(1,1,n,p+1);
        cout<<tree[1].a<<'\n';
    }
}
int main()
{
    closeSync;
    //multiCase
    {
        solve();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值