Codeforces 575A. Fibonotci 矩阵乘法+线段树

题解:

这道题……看完题就大概知道怎么做,但是考试时没有实现出来。
考试的时候想的是用倍增来实现,但是细节太多写不出来,正解是用线段树来维护连续n个矩阵的乘积,其实这个做法也挺显然的,但是没有把这两个东西放在一起用过。然后细节还是很多,调了一下午。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=50010;
const int inf=2147483647;
LL read()
{
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
struct Node{LL pos,bel;int v;}a[Maxn];
bool cmp(Node a,Node b){return a.pos<b.pos;}
LL K,belK;
int P,n,m,s[Maxn],F[Maxn];
struct Matrix{int v[2][2],r,c;}t,st,one;
Matrix operator * (Matrix a,Matrix b)
{
    Matrix c;
    memset(c.v,0,sizeof(c.v));
    c.r=b.r;c.c=a.c;
    for(int i=0;i<=c.r;i++)
    for(int j=0;j<=c.c;j++)
    for(int k=0;k<=c.r;k++)
    c.v[i][j]=(c.v[i][j]+(LL)a.v[k][j]*b.v[i][k]%P)%P;
    return c;
}
Matrix Pow(Matrix x,LL y)
{
    if(!y)return one;
    if(y==1)return x;
    Matrix t=Pow(x,y>>1),re=t*t;
    if(y&1)re=re*x;
    return re;
}
struct Seg{int l,r,lc,rc;Matrix A;}tr[Maxn<<1];
int len=0;
void build(int l,int r)
{
    int t=++len;
    tr[t].l=l;tr[t].r=r;
    if(l==r)
    {
        tr[t].A.r=tr[t].A.c=1;
        tr[t].A.v[0][0]=0,tr[t].A.v[0][1]=1;
        tr[t].A.v[1][0]=s[l-1],tr[t].A.v[1][1]=s[l%n];
        return;
    }
    int mid=l+r>>1;
    tr[t].lc=len+1,build(l,mid);
    tr[t].rc=len+1,build(mid+1,r);
    tr[t].A=tr[tr[t].lc].A*tr[tr[t].rc].A;
}
void modify(int x,int p1,int p2,int v)
{
    if(tr[x].l==tr[x].r){tr[x].A.v[1][p2]=v;return;}
    int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
    if(p1<=mid)modify(lc,p1,p2,v);
    else modify(rc,p1,p2,v);
    tr[x].A=tr[lc].A*tr[rc].A;
}
int main()
{
    one.r=one.c=1;
    one.v[0][0]=one.v[1][1]=1;
    one.v[0][1]=one.v[1][0]=0;

    K=read(),P=read();
    if(K<2)return printf("0"),0;
    n=read();
    belK=(K-2)/n+1;

    for(int i=0;i<n;i++)s[i]=read();
    build(1,n);t=tr[1].A;

    m=read();
    for(int i=1;i<=m;i++)a[i].pos=read(),a[i].v=read(),a[i].bel=(a[i].pos-1)/n+1;
    sort(a+1,a+1+m,cmp);
    while(a[m].pos>=K&&m)m--;
    if(a[m].pos<K-n&&K-n>n)a[++m].pos=K-n,a[m].v=s[(K-n)%n],a[m].bel=(K-n-1)/n+1;
    st.r=1,st.c=0;
    st.v[0][0]=0,st.v[1][0]=1;
    LL now=0;int i=1;
    while(i<=m)
    {
        if((now+1)*n+1>=K)break;
        if(now<a[i].bel-1)
        {
            if(i>1&&a[i-1].pos==now*n)
            {
                modify(1,1,0,a[i-1].v);
                t=tr[1].A;
                st=st*t;
                modify(1,1,0,s[0]);
                t=tr[1].A;
                st=st*Pow(t,a[i].bel-2-now),now=a[i].bel-1;
            }
            else st=st*Pow(t,a[i].bel-1-now),now=a[i].bel-1;
        }
        if((now+1)*n+1>=K)break;
        int j=i;
        while(j<=m&&a[j].bel==a[i].bel)j++;j--;
        if(i>1&&a[i-1].pos==now*n&&a[i-1].pos%n==0)modify(1,1,0,a[i-1].v);
        for(int k=i;k<=j;k++)
        {
            int tmp=a[k].pos%n;
            if(!tmp)modify(1,n,1,a[k].v);
            else
            {
                modify(1,tmp+1,0,a[k].v);
                modify(1,tmp,1,a[k].v);
            }
        }
        st=st*tr[1].A;
        if(i>1&&a[i-1].pos==now*n&&a[i-1].pos%n==0)modify(1,1,0,s[0]);
        for(int k=i;k<=j;k++)
        {
            int tmp=a[k].pos%n;
            if(!tmp)modify(1,n,1,s[0]);
            else
            {
                modify(1,tmp+1,0,s[tmp]);
                modify(1,tmp,1,s[tmp]);
            }
        }
        now++;
        i=j+1;
    }
    LL x,tk=K-1;
    while((tk-1)%n)tk--;
    x=(tk-1)/n;
    int t1=-1,t2=-1,cnt=0;
    for(int j=1;j<=m;j++)
    if(a[j].pos>=(belK-1)*n)
    {
        if(a[j].pos%n)s[a[j].pos%n]=a[j].v;
        else
        {
            if(a[j].pos==(belK-1)*n)t1=a[j].v;
            else t2=a[j].v;
        }
    }
    if(t1==-1)t1=s[0];
    if(t2==-1)t2=s[0];
    F[0]=st.v[0][0],F[1]=st.v[1][0];
    for(LL j=1;j<=K-(belK-1)*n-1;j++)
    {
        LL s1=s[j%n],s2=s[(j-1)%n];
        if(j==1)s2=t1;
        if(j==n)s1=t2;
        if(j==n+1)s2=t2;
        F[j+1]=((LL)F[j]*s1%P+(LL)F[j-1]*s2%P)%P;
    }
    printf("%d",F[K-(belK-1)*n]);
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值