题解 基因识别

传送门

  • 关于「给定一些 \(s\) 和一些 \(t\),每个 \(s\) 向是其子串的 \(t\) 产生贡献」:
    注意「\(t\)\(s\) 的子串」还可以被表示为两者拼接后的 \(\tt height\) 数组上的一段区间 \([l, r]\),满足 \(t\) 是区间中每个后缀的前缀
    那么就可以转化为区间数颜色问题了

然后本题带修
正解是带修莫队,我也不知道为什么能过

  • 注意带修莫队的排序和普通莫队是不同的:以左端点所在块为第一关键字,右端点所在块为第二关键字,时间为第三关键字

复杂度 \(O(n^{\frac{5}{3}})\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define ll long long
#define ull unsigned long long
//#define int long long

int n, m;
ll c[N];
ull h[N], pw[N];
int uni[N], usiz;
string s[N], t[N];
const ull base=13131;
int op[N], x[N], y[N];
inline ull hashing(int l, int r) {return h[r]-h[l-1]*pw[r-l+1];}

// namespace force{
// 	unordered_map<ull, bool> mp[3002], vis;
// 	void solve() {
// 		for (int i=1; i<=m; ++i) if (op[i]==1) {
// 			ull tem=0;
// 			for (auto& it:t[i]) tem=tem*base+it;
// 			vis[tem]=1;
// 		}
// 		for (int i=1; i<=n; ++i) {
// 			for (int j=0; j<s[i].length(); ++j) h[j+1]=h[j]*base+s[i][j];
// 			for (int j=1; j<=usiz; ++j) {
// 				int len=uni[j], siz=s[i].length();
// 				for (int k=1; k+len-1<=siz; ++k) {
// 					ull tem=hashing(k, k+len-1);
// 					if (vis.find(tem)!=vis.end()) mp[i][tem]=1;
// 				}
// 			}
// 		}
// 		for (int i=1; i<=m; ++i) {
// 			if (op[i]&1) {
// 				ll ans=0; ull tem=0;
// 				for (auto& it:t[i]) tem=tem*base+it;
// 				for (int j=1; j<=n; ++j) if (mp[j].find(tem)!=mp[j].end()) ans+=c[j];
// 				printf("%lld\n", ans);
// 			}
// 			else c[x[i]]+=y[i];
// 		}
// 	}
// }

// namespace task1{
// 	unordered_map<ull, ll> mp;
// 	unordered_map<ull, bool> now, vis;
// 	void solve() {
// 		for (int i=1; i<=m; ++i) if (op[i]==1) {
// 			ull tem=0;
// 			for (auto& it:t[i]) tem=tem*base+it;
// 			vis[tem]=1;
// 		}
// 		for (int i=1; i<=n; ++i) {
// 			now.clear();
// 			for (int j=0; j<s[i].length(); ++j) h[j+1]=h[j]*base+s[i][j];
// 			for (int j=1; j<=usiz; ++j) {
// 				int len=uni[j], siz=s[i].length();
// 				for (int k=1; k+len-1<=siz; ++k) {
// 					ull tem=hashing(k, k+len-1);
// 					if (vis.find(tem)!=vis.end() && now.find(tem)==now.end()) mp[tem]+=c[i], now[tem]=1;
// 				}
// 			}
// 		}
// 		for (int i=1; i<=m; ++i) {
// 			if (op[i]&1) {
// 				ull tem=0;
// 				for (auto& it:t[i]) tem=tem*base+it;
// 				printf("%lld\n", mp[tem]);
// 			}
// 			else c[x[i]]+=y[i];
// 		}
// 	}
// }

namespace task{
	char str[N];
	ll ans[N], now;
	int st[22][N], lg[N], bel[N], sqr;
	int pos[N], sa[N], rk[N], id[N], px[N], oldrk[N], cnt[N], ht[N], col[N], k=256, tot, top;
	struct que{int l, r, tim, id;}q[N];
	// inline bool operator < (que a, que b) {return bel[a.l]==bel[b.l]?(bel[a.l]&1?a.r<b.r:a.r>b.r):bel[a.l]<bel[b.l];}
	inline bool operator < (que a, que b) {
		if (bel[a.l]^bel[b.l]) return a.l<b.l;
		if (bel[a.r]^bel[b.r]) return a.r<b.r;
		return a.tim<b.tim;
	}
	inline bool cmp(int a, int b, int w) {return oldrk[a]==oldrk[b]&&oldrk[a+w]==oldrk[b+w];}
	inline int qmin(int l, int r) {
		if (l>r) return INF;
		int t=lg[r-l+1]-1;
		// cout<<"qmin: "<<l<<' '<<r<<' '<<min(st[t][l], st[t][r-(1<<t)+1])<<endl;
		return min(st[t][l], st[t][r-(1<<t)+1]);
	}
	inline void add(int i) {if (++cnt[col[sa[i]]]==1) now+=c[col[sa[i]]];}
	inline void del(int i) {if (--cnt[col[sa[i]]]==0) now-=c[col[sa[i]]];}
	void solve() {
		for (int i=1; i<=n; ++i) {
			for (auto& it:s[i]) str[++tot]=it, col[tot]=i;
			str[++tot]='#';
		}
		for (int i=1; i<=m; ++i) if (op[i]&1) {
			pos[i]=tot+1;
			for (auto& it:t[i]) str[++tot]=it;
			str[++tot]='#';
		}
		// cout<<str+1<<endl;
		for (int i=1; i<=tot; ++i) ++cnt[rk[i]=str[i]];
		for (int i=1; i<=k; ++i) cnt[i]+=cnt[i-1];
		for (int i=1; i<=tot; ++i) sa[cnt[rk[i]]--]=i;
		for (int w=1,p; ; w<<=1,k=p) {
			p=0;
			for (int i=tot; i>tot-w; --i) id[++p]=i;
			for (int i=1; i<=tot; ++i) if (sa[i]>w) id[++p]=sa[i]-w;
			for (int i=1; i<=k; ++i) cnt[i]=0;
			for (int i=1; i<=tot; ++i) ++cnt[px[i]=rk[id[i]]];
			for (int i=1; i<=k; ++i) cnt[i]+=cnt[i-1];
			for (int i=tot; i; --i) sa[cnt[px[i]]--]=id[i];
			for (int i=1; i<=tot; ++i) oldrk[i]=rk[i];
			p=0;
			for (int i=1; i<=tot; ++i) rk[sa[i]]=cmp(sa[i], sa[i-1], w)?p:++p;
			if (p==tot) break;
		}
		for (int i=1,k=0; i<=tot; ++i) {
			if (k) --k;
			while (str[i+k]==str[sa[rk[i]-1]+k]) ++k;
			ht[rk[i]]=k;
		}
		// cout<<"---sa---"<<endl; for (int i=1; i<=tot; ++i) {cout<<setw(2)<<i<<" h("<<setw(1)<<col[sa[i]]<<"): "; for (int j=sa[i]; j<=tot; ++j) cout<<str[j]; cout<<endl;}
		for (int i=1; i<=tot; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
		for (int i=1; i<=tot; ++i) st[0][i]=ht[i];
		int t=lg[tot]-1;
		for (int i=1; i<=t; ++i)
			for (int j=1; j+(1<<i)-1<=tot; ++j)
				st[i][j]=min(st[i-1][j], st[i-1][j+(1<<i-1)]);
		for (int i=1; i<=m; ++i) if (op[i]&1) {
			// cout<<"i: "<<i<<endl;
			int l, r, tl, tr, mid;
			// cout<<"pos: "<<rk[pos[i]]<<endl;
			tl=1, tr=rk[pos[i]];
			while (tl<=tr) {
				mid=(tl+tr)>>1;
				if (qmin(mid+1, rk[pos[i]])>=::t[i].length()) tr=mid-1;
				else tl=mid+1;
			}
			l=tr+1;
			tl=rk[pos[i]], tr=tot;
			while (tl<=tr) {
				mid=(tl+tr)>>1;
				if (qmin(rk[pos[i]]+1, mid)>=::t[i].length()) tl=mid+1;
				else tr=mid-1;
			}
			r=tl-1;
			// cout<<"t: "<<::t[i]<<endl;
			// cout<<"lr: "<<l<<' '<<r<<endl;
			++top, q[top]={l, r, i, top};
		}
		// cout<<"top: "<<top<<endl;
		sqr=pow(m, 0.666);
		for (int i=1; i<=tot; ++i) bel[i]=(i-1)/sqr+1;
		sort(q+1, q+top+1);
		memset(cnt, 0, sizeof(cnt));
		for (int i=1,l=0,r=0,k=0; i<=top; ++i) {
			while (l>q[i].l) add(--l);
			while (r<q[i].r) add(++r);
			while (l<q[i].l) del(l++);
			while (r>q[i].r) del(r--);
			while (k<q[i].tim) if (!(op[++k]&1)) {
				if (cnt[x[k]]) now+=y[k];
				c[x[k]]+=y[k];
			}
			while (k>q[i].tim) if (!(op[k--]&1)) {
				if (cnt[x[k+1]]) now-=y[k+1];
				c[x[k+1]]-=y[k+1];
			}
			ans[q[i].id]=now;
		}
		for (int i=1; i<=top; ++i) printf("%lld\n", ans[i]);
	}
}

signed main()
{
	freopen("dna.in", "r", stdin);
	freopen("dna.out", "w", stdout);
	ios::sync_with_stdio(0);
	
	cin>>n>>m;
	for (int i=1; i<=n; ++i) cin>>c[i]>>s[i];
	for (int i=1; i<=m; ++i) {
		cin>>op[i];
		if (op[i]&1) cin>>t[i], uni[++usiz]=t[i].length();
		else cin>>x[i]>>y[i];
	}
	sort(uni+1, uni+usiz+1);
	usiz=unique(uni+1, uni+usiz+1)-uni-1;
	pw[0]=1;
	for (int i=1; i<N; ++i) pw[i]=pw[i-1]*base;
	// if (n<=3000) force::solve();
	// else task1::solve();
	task::solve();

	return 0;
}
posted @ 2022-07-06 15:55  Administrator-09  阅读(2)  评论(0编辑  收藏  举报