题意:给两个长度为n的数字串,a,b每次可以给a串任意长度区间按非递减排序,问a串是否能变为b串。
思路:一开始写的时候,贪心思路错=。= 看了看别人贪心的思路然后过了,做法是依次枚举b串上的每个数字,然后在a串上依次寻找里b串最近的位置k,然后判断在a串中[1,k]这个区间中,这个数字是不是最小值,如果是ok,否则no,在a中判断成立的数字要去掉。(这个贪心emm,总感觉哪里怪怪的,没法证明)
搞定贪心的思路就好做了,给a串建个线段树,用来查找[1,k]的最小值,更新去掉的点为INF,然后用队列记录a串中第一次出现b中数字的位置。
#include<bits/stdc++.h> using namespace std; const int maxn=3e5+5; int a[maxn]; int b[maxn]; const int INF=0x3f3f3f3f; struct note { int left, right, minn; } tree[maxn*4]; queue<int> que[maxn]; void pushup(int id) { tree[id].minn=min(tree[id<<1].minn,tree[id<<1|1].minn); } void build(int id, int l, int r) { tree[id].left=l; tree[id].right=r; if(l==r) { tree[id].minn=a[l]; que[a[l]].push(l); } else { int mid=(l+r)/2; build(id<<1, l, mid); build(id<<1|1, mid+1, r); pushup(id); } } int query(int id, int l, int r) { if(l<=tree[id].left && tree[id].right<=r) return tree[id].minn; else { int minn=INF; int mid=(tree[id].left+tree[id].right)/2; if(l<=mid) minn=min(minn,query(id<<1, l, r)); if(r>mid) minn=min(minn,query(id<<1|1, l, r)); return minn; } } void update(int id, int pos, int val) { if(tree[id].left==tree[id].right) tree[id].minn=val; else { int mid=(tree[id].left+tree[id].right)/2; if(pos<=mid) update(id<<1, pos, val); else update(id<<1|1, pos, val); pushup(id); } } int main() { int t; scanf("%d",&t); while(t--) { int n; scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); while(!que[a[i]].empty()) { que[a[i]].pop(); } } build(1,1,n); for(int i=1; i<=n; i++) scanf("%d",&b[i]); int flag=1; for(int i=1; i<=n; i++) { int k; if(!que[b[i]].empty()) { k=que[b[i]].front(); que[b[i]].pop(); } else flag=0; int minn=query(1,1,k); // printf("%d %d\n",k,minn); if(minn!=b[i]) flag=0; else update(1,k,INF); if(flag==0) break; } if(flag) printf("YES\n"); else printf("NO\n"); } }
原文地址:https://www.cnblogs.com/dongdong25800/p/11124792.html
时间: 2024-10-11 01:15:11