传送门
题意:给定一个n个数的数字序列,第i个数为a[i],每次操作会将a[i]插入或移到最前端:
1.若a[i]已经在序列中出现过,则将其移到最前端,并删除原出现位置
2.若a[i]未出现过,则直接将其插入到最前端
有q个询问,每个询问给出一个长度为m的序列,问是否在某个时刻询问序列与操作的序列相同,忽略后缀的0
(就是操作系统中的LRU,刚好在学
解:由于物理块的个数不知道,我们可以忽略最近最久未使用的删除操作,只执行上述将其移动到最前面的操作;在模拟之前我们将q次查询的序列插入字典树,这里的字典树用unordered_map来操作就好了,记录每个序列的结束节点;然后我们就模拟lru,每一次循环,就在字典树中跑一次当前的list,跑到的点就vis为1,这样我们最后只要看结束节点就可以了(用stl是很方便,不过被卡了就很难受了。
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=2e6+5;
template <typename _Tp> il void read(_Tp&x) {
char ch;bool flag=0;x=0;
while(ch=getchar(),!isdigit(ch)) if(ch=='-')flag=1;
while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
if(flag) x=-x;
}
//il int Add(int &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(int &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int T,n,q,a[N],len[N],b[N];
unordered_map<int,int> tr[N];
int cnt,root;
bool vis[N];
int newnode(){
++cnt;
tr[cnt].clear(),vis[cnt]=0;
return cnt;
}
void init(){
cnt=0;
root=newnode();
}
int add(int len){
int rt=root;
for(int i=1;i<=len;++i){
if(!tr[rt].count(b[i])){
tr[rt][b[i]]=newnode();
}
rt=tr[rt][b[i]];
}
return rt;//结点坐标
}
int ls[N];
list<int>::iterator mp[N];
list<int> li;
void run(){
li.clear();
for(int i=1;i<=n;++i) mp[a[i]]=li.end();
for(int i=1;i<=n;++i){
if(mp[a[i]]!=li.end()){
li.erase(mp[a[i]]);
}
li.push_front(a[i]);
mp[a[i]]=li.begin();
int rt=1;
for(auto u:li){
if(!tr[rt].count(u)){
break;
}
rt=tr[rt][u];
vis[rt]=1;
}
while(tr[rt].count(0)){
rt=tr[rt][0];
vis[rt]=1;
}
}
//检查所有查询串的结束节点
for(int i=1;i<=q;++i){
if(vis[ls[i]]) printf("Yes\n");
else printf("No\n");
}
}
int main(){
// std::ios::sync_with_stdio(0);cin.tie(0);
read(T);
while(T--){
init();
read(n),read(q);
for(int i=1;i<=n;++i) read(a[i]);
for(int i=1;i<=q;++i){
read(len[i]);
for(int j=1;j<=len[i];++j) read(b[j]);
ls[i]=add(len[i]);
}
run();
}
return 0;
}