1. 程式人生 > 其它 >題解 P5335 [THUSC2016]補退選

題解 P5335 [THUSC2016]補退選

\[\text{題目大意} \]

\(\quad\)本題就是每次對一個字元執行 \(3\) 種操作:

  • 操作1:插入一個字串 \(s\)
  • 操作2:彈出一個字串 \(s\)
  • 操作3:詢問以字串 \(s\) 為字首的數量最早在哪個事件後 超過 \(x\)

注意:

  1. 同一時刻可以存在多個完全相同的字串。
  2. 保證刪除的數存在。
  3. 操作的編號就是事件編號,包括操作1,2,3。
  4. 另外操作強制線上,上一次的答案要取絕對值。
  5. 好像要開 long long,否則會WA

\(\quad\)因為是詢問字串存在的問題,考慮使用字典樹求解,用 \(sz_i\) 表示編號為 \(i\) 的點,每次插入字串路徑上的點 \(sz_i+1\) ,刪除字串時字串路徑上的點 \(sz_i-1\)

(基本操作)。

\(\quad\)因為字串字首數量一定是逐漸變大,不可能一次從 \(1\) 個加到 \(10\) 個,所以用 vector num_{i,j} 儲存字串編號為 \(i\) 的點第一次超過 \(j\) 數量的最早的事件編號,詢問時先判斷歷史上是否存在,如果 vector 的大小小於 \(x\),就返回。

本題是真的有點水

//P5335 [THUSC2016]補退選
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#define int long long
#define re register int
#define il inline
using namespace std;
il int read()
{
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)&&ch!='-')ch=getchar();
  if(ch=='-')f=-1,ch=getchar();
  while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
  return x*f;
}
il void print(int x)
{
  if(x<0)putchar('-'),x=-x;
  if(x/10)print(x/10);
  putchar(x%10+'0');
}
const int N=1e5+5,M=6e6+5;
int n,ch[M][10],last,sz[M],cnt;
vector<int>num[M];
char s[65];
il void insert(int id)
{
	scanf("%s",s+1);
	int len=strlen(s+1),u=0;
	for(re i=1;i<=len;i++)
	{
		int x=s[i]-'a';
		if(!ch[u][x])ch[u][x]=++cnt;
		u=ch[u][x];sz[u]++;
		if(sz[u]>num[u].size())num[u].push_back(id);//第一次超過
	}
}
il void del()
{
	scanf("%s",s+1);
	int len=strlen(s+1),u=0;
	for(re i=1;i<=len;i++)
	{
		int x=s[i]-'a';
		u=ch[u][x];sz[u]--;
	}
}
il void query()
{
	scanf("%s",s+1);
	int len=strlen(s+1),u=0;
	int x=read(),y=read(),z=read();
	x=(x*last+y)%z;
	for(re i=1;i<=len;i++)
	{
		int x=s[i]-'a';
		if(!ch[u][x]){last=1;puts("-1");return;}
		u=ch[u][x];
	}
	if((long long)num[u].size()>x)print(last=num[u][x]),putchar('\n');
	else puts("-1"),last=1;
}
signed main()
{
	n=read();
	for(re i=1;i<=n;i++)
	{
		int op=read();
		if(op==1)insert(i);
		else if(op==2)del();
		else query();
	}
	return 0;
}