题目描述:https://www.luogu.com.cn/problem/P4692
题解
٩(๑>◡<๑)۶ 人生第二道YNOI ٩(๑>◡<๑)۶
希望有生之年能刷完YNOI
这道题的思路并不难想
很明显我们可以对每种数分别求出它们的贡献,在加起来就是答案(con.size()*mlen-ans)(con表示迄今为止出现了多少种数)
对于一种数(v)怎么求贡献?
正难则反,用总方案(mlen)减去不合法的方案(mul[v])
怎么求一种数的不合法方案数?
求出该种数在每个序列的不合法方案数(sum[i][v]),再乘起来就是不合法的方案总数(mul[v])
怎么求一种数在一个序列里的不合法方案数?
对这种数开一个set (mp[i][v]),记录一下出现的坐标,把相邻两个坐标之间的子区间(不包含这两个坐标)数全部加起来即可
代码写起来太麻烦了
而且有一个巨坑无比的细节:sum[i][v]可能为0(即序列i的所有数都为v),如果将其乘进mul[v]的话,会导致后面将要除0
这怎么办?
我们可以记录一下,对于数v,有几个序列的sum[i][v]为0,记为zer[v]
在更新的时候注意判断一下zer[v],只有zer[v]为0才更新ans(ans表示所有数的不合法方案数之和)
接下来就是鬼畜卡常数时间(为什么每次做YNOI的题都要卡常数)
ε=(´ο`*)))唉,大常数选手的叹息
先优化了一些显而易见的重复计算
然后各种优化取模的常数,发现在加的时候可以不用*1ll
又发现自己一次insert要查询十几次map
于是用&优化了一下,就只用查5次map了
然后TLE 1.56s
尝试预处理逆元,被卡空间了
后来才发现可以直接离散化优化掉con,zer,mul三个map(由于比较懒,没有把con优化掉(因为代码中利用了一下map[]访问一次空点就会多一个点的性质,不太好搞))
然后就A了
最慢的一个1.21s
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return flg==1?num:-num;
}
#define N 100005
const int mod=19260817;
const int inv2=9630409;
int inv(int x)
{
if(!x) return 1;
int ret=1,y=mod-2;
while(y){
if(y&1)ret=1ll*ret*x%mod;
y>>=1;x=1ll*x*x%mod;
}
return ret;
}
int n,m,ans,len[N],cal[N],mlen;
vector<int> a[N];
map<int,set<int> >mp[N];
set<int>::iterator it,it1,it2;
map<int,int> sum[N],con;
int hh[2*N],hcnt;
struct node{int i,j,z;}q[N];
int mul[2*N],zer[2*N];
void insert(int i,int j,int v)
{
int &mu=mul[v],&su=sum[i][v],tm=mu,ts=su;
set<int> &S=mp[i][v];
if(!con[v]++)mu=mlen;
if(S.empty()){
S.insert(-1);S.insert(len[i]);
ts=su=cal[len[i]+1];
}
it1=S.lower_bound(j);
it2=it1;it1--;
su=(su-cal[(*it2)-(*it1)]+cal[(*it2)-j]+cal[j-(*it1)]+mod)%mod;
S.insert(j);
if(!su){if(!zer[v]++){ans=ans-tm+mod;if(ans>=mod)ans-=mod;}}
else{
mu=1ll*mu*inv(ts)%mod*su%mod;
if(!zer[v])ans=(ans-tm+mu+mod)%mod;
}
}
void delet(int i,int j,int v)
{
int &mu=mul[v],&su=sum[i][v],tm=mu,ts=su;
set<int> &S=mp[i][v];
it1=it2=it=S.lower_bound(j);
it1--;it2++;
su=(su+cal[(*it2)-(*it1)]-cal[(*it2)-j]-cal[j-(*it1)]+2*mod)%mod;
S.erase(it);con[v]--;
if(!ts){if(!(--zer[v])){ans=ans+mu;if(ans>=mod)ans-=mod;}}
else{
mu=1ll*mu*inv(ts)%mod*su%mod;
if(!zer[v])ans=(ans-tm+mu+mod)%mod;
}
}
int main()
{
int i,j,z;
mlen=1;ans=0;
for(cal[1]=0,i=1;i<=100000;i++)
cal[i+1]=(cal[i]+i)%mod;
n=gi();m=gi();
for(i=1;i<=n;i++){
len[i]=gi();
mlen=1ll*mlen*cal[len[i]+1]%mod;
}
for(i=1;i<=n;i++)for(j=0;j<len[i];j++){
a[i].push_back(gi());
hh[++hcnt]=a[i][j];
}
for(i=1;i<=m;i++){
q[i].i=gi();q[i].j=gi()-1;q[i].z=gi();
hh[++hcnt]=q[i].z;
}
sort(hh+1,hh+hcnt+1);
hcnt=unique(hh+1,hh+hcnt+1)-hh-1;
for(i=1;i<=n;i++)for(j=0;j<len[i];j++){
a[i][j]=lower_bound(hh+1,hh+hcnt+1,a[i][j])-hh;
insert(i,j,a[i][j]);
}
printf("%d\n",(1ll*con.size()*mlen%mod+mod-ans)%mod);
for(int k=1;k<=m;k++){
i=q[k].i;j=q[k].j;
z=q[k].z=lower_bound(hh+1,hh+hcnt+1,q[k].z)-hh;
delet(i,j,a[i][j]);
insert(i,j,a[i][j]=z);
printf("%d\n",(1ll*con.size()*mlen%mod+mod-ans)%mod);
}
}