題解 P5335 [THUSC2016]補退選
阿新 • • 發佈:2021-10-25
\[\text{題目大意}
\] (基本操作)。
\(\quad\)本題就是每次對一個字元執行 \(3\) 種操作:
- 操作1:插入一個字串 \(s\) 。
- 操作2:彈出一個字串 \(s\) 。
- 操作3:詢問以字串 \(s\) 為字首的數量最早在哪個事件後 超過 \(x\) 。
注意:
- 同一時刻可以存在多個完全相同的字串。
- 保證刪除的數存在。
- 操作的編號就是事件編號,包括操作1,2,3。
- 另外操作強制線上,上一次的答案要取絕對值。
- 好像要開 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; }