peiwenjun's blog 没有知识的荒原

P4692 [Ynoi2016] 谁的梦 题解

题目描述

定义一个序列的权值为不同数字的个数。

给定 \(n\) 个序列,从每个序列中选择一个子串并拼接起来,求所有选法得到的序列权值之和。

如果一个序列能通过多种选法得到,那么计算多次。

接下来 \(m\) 次修改操作 x y z ,表示将第 \(x\) 个序列的第 \(y\) 个数修改为 \(z\)

输出修改前和每次修改后的答案对 \(19260817\) 取模的结果。

数据范围

  • \(1\le n,m,\sum|len_i|\le 10^5\)
  • \(1\le x\le n,1\le y\le len_x\) ,保证任何时刻序列中的元素均为 int 型整数。

时间限制 \(\texttt{1.5s}\) ,空间限制 \(\texttt{125MB}\)

分析

显然可以把贡献拆到每个元素上。

  • \(f_{i,x}\)vector 类型)存储第 \(i\) 个序列中 \(x\) 的所有出现位置。
  • \(g_{i,x}=\sum\binom{f_{i,x,j}-f_{i,x,j-1}}2\) 为从第 \(i\) 个序列中选择一个不含 \(x\) 的方案数。
  • \(h_x=\prod_{i=1}^ng_{i,x}\)

则最终答案为:

\[res=\sum\left(\frac{len_i(len_i+1)}2-h_x\right)\\ \]

容易发现修改只会对 \(\mathcal O(1)\) 个值产生影响。先倒序撤销对 \(res\) 的贡献,再正序更新即可。

为了解决 \(h_x\) 乘除 \(0\) 的问题,将 \(h_x\) 维护成 \(v\cdot 0^c\) 的形式,其中 \(v\neq 0\)

时间复杂度 \(\mathcal O(n\log n)\) ,细节较多。

#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=1e5+5,mod=19260817;
int m,n,x,y,z,all=1,res;
int l[maxn];
vector<int> vec[maxn];
map<int,set<int>> f[maxn];
map<int,int> g[maxn];
int qpow(int a,int k)
{
    int res=1;
    for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
    return res;
}
struct num
{
    int o,x;
    num()
    {
        o=0,x=all;
    }
    int val()
    {
        return o?0:x;
    }
};
void operator*=(num &a,int b)
{
    b?a.x=1ll*a.x*b%mod:a.o++;
}
void operator/=(num &a,int b)
{
    b?a.x=1ll*a.x*qpow(b,mod-2)%mod:a.o--;
}
map<int,num> h;
void inc(int &x,int y)
{
    if((x+=y)>=mod) x-=mod;
}
void dec(int &x,int y)
{
    if((x-=y)<0) x+=mod;
}
int c(int x)
{
    return x*(x-1ll)/2%mod;
}
void del(int i,int j)
{
    int x=vec[i][j];
    auto &s=f[i][x];
    auto r=s.erase(s.find(j)),l=prev(r);
    inc(res,h[x].val()),h[x]/=g[i][x];
    dec(g[i][x],c(j-*l)),dec(g[i][x],c(*r-j)),inc(g[i][x],c(*r-*l));
    h[x]*=g[i][x],dec(res,h[x].val());
}
void add(int i,int j)
{
    int x=vec[i][j];
    if(g[i].find(x)==g[i].end()) f[i][x].insert(-1),f[i][x].insert(l[i]),g[i][x]=c(l[i]+1);
    auto &s=f[i][x];
    auto it=s.insert(j).fi,l=prev(it),r=next(it);
    inc(res,h[x].val()),h[x]/=g[i][x];
    inc(g[i][x],c(j-*l)),inc(g[i][x],c(*r-j)),dec(g[i][x],c(*r-*l));
    h[x]*=g[i][x],dec(res,h[x].val());
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&l[i]),all=1ll*all*c(l[i]+1)%mod;
    for(int i=1;i<=n;i++)
    {
        vec[i].resize(l[i]);
        for(int j=0;j<l[i];j++) scanf("%d",&vec[i][j]),f[i][vec[i][j]].insert(j);
        for(auto &p:f[i])
        {
            int x=p.fi,cur=0;
            auto &s=p.se;
            s.insert(-1),s.insert(l[i]);
            for(auto it=s.begin();it!=s.end();++it) if(it!=s.begin()) inc(cur,c(*it-*prev(it)));
            g[i][x]=cur,h[x]/=c(l[i]+1),h[x]*=cur;
        }
    }
    res=1ll*h.size()*all%mod;
    for(auto &p:h) dec(res,p.se.val());
    printf("%d\n",res);
    while(m--)
    {
        scanf("%d%d%d",&x,&y,&z),del(x,--y),vec[x][y]=z,add(x,y);
        printf("%d\n",res);
    }
    return 0;
}

posted on 2025-02-07 10:53  peiwenjun  阅读(17)  评论(0)    收藏  举报

导航