题目描述: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);
	}
}