trichlorotrifluoroethane
P9754 [CSP-S 2023] 结构体
一年的痛终于解决。
一个结构体的对齐要求为其成员的对齐要求的 \(\gcd\),其大小为大于等于实际大小的最小整除对齐要求的数,基础类型的对齐要求为其大小。
给你一个无限长的内存,头地址为 \(0\),支持以下操作:
X k t1 n1...tk nk
声明一个结构体名字为 \(X\) 包含 \(k\) 个成员,第 \(i\) 个成员的类型为 \(t_i\),名字为 \(n_i\)。输出结构体大小和对齐要求;t n
定义一个变量名字为 \(n\),类型为 \(t\)。输出其开始地址;s
求变量 \(s\) 的开始地址;addr
求包含地址 \(addr\) 的变量名(精确到基础类型层面)。
按 要 求 模 拟 即 可,时间复杂度 \(O(可过)\)。3、4 操作莫名有种求排名、第 \(k\) 小的感觉?
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int maxaddress;
const int maxn=203;
int num; // 类型数
struct info{
int type;
string name;
int pos; // 起始位置
bool operator<(const info &o)const{
return pos<o.pos;
}
};
struct node{
vector<info>pb; // 包含的类型编号
int byd; // 使用字节数
int lim; // 对齐要求
map<string,int>t;// 成员名转编号
}type[maxn];
map<string,int>mp; // 类型名转编号
map<string,int>gp; // 变量名转编号
int n;
void def_struct(string s,int k){
string t,r;
mp[s]=++num;
for(int i=1;i<=k;i++){
cin>>t>>r;
int ty=mp[t];
node now=type[ty];
int lastpos=((type[num].byd-1)/now.lim+1)*now.lim;
if(i==1) lastpos=0;
type[num].t[r]=i-1;
type[num].pb.push_back({ty,r,lastpos});
type[num].lim=max(type[num].lim,type[ty].lim);
type[num].byd=lastpos+now.byd;
}
type[num].byd=((type[num].byd-1)/type[num].lim+1)*type[num].lim;
cout<<type[num].byd<<' '<<type[num].lim<<'\n';
}
int cnt;
info memory[maxn]; // 内存中的变量
void def_var(string t,string r){
node now=type[mp[t]];
int lastpos=((maxaddress-1)/now.lim+1)*now.lim;
if(!cnt) lastpos=0;
gp[r]=++cnt;
memory[cnt]={mp[t],r,lastpos};
maxaddress=lastpos+now.byd;
cout<<lastpos<<'\n';
}
int find_pos(string s,int x){
return type[x].pb[type[x].t[s]].pos;
}
int find_pot(int s,int x){
return type[x].pb[s].pos+type[type[x].pb[s].type].byd;
}
int dfs1(string s,int fa){
int pos=s.find(".");
if(pos==EOF){
return find_pos(s,fa);
}
string tmp=s.substr(0,pos);
return find_pos(tmp,fa)+dfs1(s.substr(pos+1),type[fa].pb[type[fa].t[tmp]].type);
}
string S;
void dfs2(int now,int fa){
if(fa<=4) return;
int pos=upper_bound(type[fa].pb.begin(),type[fa].pb.end(),(info){0,"",now})-type[fa].pb.begin()-1;
if(now<find_pot(pos,fa)){
S+='.';
S+=type[fa].pb[pos].name;
dfs2(now-type[fa].pb[pos].pos,type[fa].pb[pos].type);
}else{
S="ERR";
}
}
signed main(){
type[++num].byd=1;
type[num].lim=1;// byte
mp["byte"]=num;
type[num].pb.push_back({0,"",0});
type[++num].byd=2;
type[num].lim=2;// short
mp["short"]=num;
type[num].pb.push_back({0,"",0});
type[++num].byd=4;
type[num].lim=4;// int
mp["int"]=num;
type[num].pb.push_back({0,"",0});
type[++num].byd=8;
type[num].lim=8;// long
mp["long"]=num;
type[num].pb.push_back({0,"",0});
cin>>n;
while(n--){
int opt,k,addr;
string s,t,r;
cin>>opt;
if(opt==1){
cin>>s>>k;
def_struct(s,k);
}else if(opt==2){
cin>>t>>r;
def_var(t,r);
}else if(opt==3){
cin>>s;
int pos=s.find(".");
if(pos==EOF){
cout<<memory[gp[s]].pos<<'\n';
continue;
}
string tmp=s.substr(0,pos);
cout<<memory[gp[tmp]].pos+dfs1(s.substr(pos+1),memory[gp[tmp]].type)<<'\n';
}else{
S.clear();
cin>>addr;
int pos=upper_bound(memory+1,memory+cnt+1,(info){0,"",addr})-memory-1;
if(addr<memory[pos].pos+type[memory[pos].type].byd){
S=memory[pos].name;
dfs2(addr-memory[pos].pos,memory[pos].type);
}else{
S="ERR";
}
cout<<S<<'\n';
}
}
return 0;
}
P9118 [春季测试 2023] 幂次
给出 \(n,k\),\(S=\{x\in [1,n]\mid x=t^w,t\in[1,n],w\ge k\}\),求 \(|S|\)。
\(n\le 10^{18},k\le 100\)
\(k=1\) 直接输出 \(n\),\(k\ge 3\) 时,\(t\) 最大 \(\sqrt[k]{n}=10^6\),不会 T。
当 \(k=2\) 时,考虑容斥。先把所有完全平方数加上,再加上其他的,注意判重,当且仅当 \(2\nmid w\) 时有贡献,时间复杂度为 \(O(\sqrt[3]{n})\)。
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e6+3;
const int maxv=1e6;
const int maxp=1e9;
int n,cnt,C,k;
int pr[maxn];
bool isp[maxn];
signed main(){
ios::sync_with_stdio(0);
cin>>n>>k;
int base=0;
for(int i=2;i<=maxv;i++){
__int128 now=i;base=1;
if(isp[now]) continue;
while(1){
base++;
now=now*i;
if(base>=k&&now<=n){
if(now<=n&&base%k==0){
if(now<=maxv) isp[now]=1;
continue;
}
cnt++;
}
if(now<=maxv) isp[now]=1;
if(now>=n) break;
}
}
cnt+=powl(n,1.0/k)-1;
cout<<cnt+1;
return 0;
}
双倍经验:[ABC361F] x = a^b
P9869 [NOIP 2023] 三值逻辑
\(\neg\texttt{T}=\texttt{F},\neg\texttt{F}=\texttt{T},\neg\texttt{U}=\texttt{U}\)
给出 \(m\) 次操作:
+ a b
\(x_a\leftarrow x_b\)- a b
\(x_a\leftarrow \neg x_b\)T/F/U a
\(x_a\leftarrow \texttt{T/F/U}\)
设操作前序列为 \(x\),操作后为 \(x'\)。当满足 \(x=x'\) 时,求可能的最少的 \(\texttt{U}\) 的数量。
\(n,m\le 10^5\)
考虑建图。将 \(\neg x\) 转化为 \(-x\),\(\texttt{U}=0,\texttt{T}=-\texttt{F}=n+1\)。对于在 \(\texttt{T/F/U}\) 的联通块中的点,其状态一定确定,其次,若在操作中出现 \(x=-x\)(合并时出现奇环) 的情况,则 \(x=\texttt{U}\)。用并查集模拟合并/二分图匹配即可。时间复杂度 \(O(n+m)\)。
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+3;
int id,T,fa[maxn+3],n,m;
char ch[maxn];
int a[maxn],b[maxn],vis[maxn];
int t,f,u;
int find(int x){
int res;
if(x==t||x==f) res=x;
else if(vis[-x+n]||x==u) res=u;
else if(vis[ x+n]) res=t;
else if(x<0){
if(x==-fa[-x]) res=x;
else{
vis[ x+n]=1;
res=find(-fa[-x]);
vis[ x+n]=0;
}
}else{
if(x==fa[x]) res=x;
else{
vis[ x+n]=1;
res=fa[x]=find(fa[x]);
vis[ x+n]=0;
}
}
return res;
}
void solve(){
cin>>n>>m;
t=n+1;f=-t;u=0;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++){
cin>>ch[i]>>a[i];
if(ch[i]=='+'||ch[i]=='-') cin>>b[i];
}
for(int i=1;i<=m;i++){
if(ch[i]=='+'||ch[i]=='-'){
if(ch[i]=='+'){
fa[a[i]]=fa[b[i]];
}else{
fa[a[i]]=-fa[b[i]];
}
}else{
if(ch[i]=='T') fa[a[i]]=t;
else if(ch[i]=='F') fa[a[i]]=f;
else fa[a[i]]=u;
}
}
int cnt=0;
for(int i=1;i<=n;i++)
if(find(i)==u) cnt++;
cout<<cnt<<'\n';
}
signed main(){
cin>>id>>T;
while(T--){
solve();
}
return 0;
}
P11188「KDOI-10 / SCP-S 2024」商店砍价
给你一个大数 \(n\),设 \(n_i\) 为 \(n\) 的第 \(i\) 位数字,进行以下任一操作:
- 删除任意一位 \(n_i\),代价为 \(c_{n_i}\);
- 删除整个数,代价为 \(n\)。
求当 \(n\) 被删完后的最小代价。多组测试。
\(n\le 10^{10^5},\forall i\in[1,9],c_i\le 10^5\),\(n\) 中仅包含数字 \([1,9]\)
有一个比较显然的贪心,在前若 \(k\) 次操作中一定是操作 1,因为这时操作 2 的代价远大于操作 1。其实这个阈值很小大概取 \(k=\lg 10^5 +1=6\) 差不多。这样我们只要枚举 \(k\) 个不进行操作 1 的位置,直接上 DP 即可,设 \(f(i,t)\) 表示在 \(i\) 右边(低位)有 \(t\) 个位置不进行操作 1,则有转移
时间复杂度 \(O(Tk\sum \lg n)\)。
code
#include<bits/stdc++.h>
#define int long long
const int maxn=1e5+3;
const int inf=0x3f3f3f3f3f3f3f3f;
using namespace std;
int id,T,v[maxn],f[10][maxn];
char n[maxn];
int powa[10];
void solve(){
cin>>n;
int lenn=strlen(n);
for(int i=1;i<=9;i++){
cin>>v[i];
}
for(int i=lenn;i;i--){
f[0][i]=f[0][i+1]+v[n[i-1]-'0'];
}
for(int i=lenn;i;i--){
for(int t=1;t<=min(lenn,6ll);t++){
f[t][i]=min(f[t-1][i+1]+powa[t-1]*(n[i-1]-'0'),f[t][i+1]+v[n[i-1]-'0']);
}
}
int ans=inf;
for(int t=0;t<=min(lenn,6ll);t++){
ans=min(ans,f[t][1]);
for(int i=1;i<=lenn;i++) f[t][i]=0;
}
cout<<ans<<'\n';
}
signed main(){
cin>>id>>T;
powa[0]=1;
for(int i=1;i<=7;i++){
powa[i]=powa[i-1]*10;
}
while(T--){
solve();
}
return 0;
}
P5684 [CSP-J 2019 JX] 非回文串
给你一个字符串 \(s\),求将 \(s\) 重排后不是回文串的方案,设 \(p_i\) 为 \(s_i\) 的位置,两个方案不同当且仅当存在一个位置 \(j\) 满足 \(p_j\neq p_j'\)。
\(|s|\le 2000\)
考虑容斥去求是回文串的方案,设 \(t_c\) 为字符 \(c\) 出现次数,考虑前 \(n/2\) 个位置填的方案,为 \(w=\prod\limits_{i=0}^{25}t_i!\binom{n/2-\sum\limits_{j<i}t_j}{t_i/2}\),则答案为 \(n!-w\)。时间复杂度 \(O(n+|\Sigma|)\)。CCF 应该是为了通过率才把数据范围开这么小罢()
code
#include<bits/stdc++.h>
#define int long long
const int maxn=2003;
const int mod=1e9+7;
using namespace std;
int n;
int ton[26],fac[maxn],ifac[maxn];
int qpow(int a,int b){
int res=1;
for(;b;b>>=1,a=a*a%mod) if(b&1) res=res*a%mod;
return res;
}
int C(int a,int b){
if(a<b) return 0;
return fac[a]*ifac[b]%mod*ifac[a-b]%mod;
}
signed main(){
cin>>n;
fac[0]=1;
for(int i=1;i<=n;i++){
fac[i]=fac[i-1]*i%mod;
}
ifac[n]=qpow(fac[n],mod-2);
for(int i=n-1;~i;i--)
ifac[i]=ifac[i+1]*(i+1)%mod;
int xo=0;
for(int i=1;i<=n;i++){
char ch;
cin>>ch;
ton[ch-'a']++;
xo^=(1<<(ch-'a'));
}
if(__builtin_popcount(xo)>1){
cout<<fac[n];
}else if(__builtin_popcount(xo)==1){
int mid=log2(xo)+0.01,ans=ton[mid],tt=0;
ton[mid]--;
for(int i=0;i<26;i++){
ans=(ans*C(n/2-tt,ton[i]/2)%mod*fac[ton[i]]%mod)%mod,tt+=ton[i]/2;
}
cout<<(fac[n]-ans+mod)%mod;
}else{
int ans=1,tt=0;
for(int i=0;i<26;i++){
ans=(ans*C(n/2-tt,ton[i]/2)%mod*fac[ton[i]])%mod,tt+=ton[i]/2;
}
cout<<(fac[n]-ans+mod)%mod;
}
return 0;
}
P9753 [CSP-S 2023] 消消乐
给你一个字符串 \(s\),求其可以被消除的子串数。
\(n\le 2\times 10^6\)
考虑分治,对于每个区间 \([l,r]\) 统计越过 \(mid\) 的答案。即可把 \([l,mid]\) 的后缀消除后的字符串用哈希存下来,然后 \([mid+1,r]\) 的字符一边消除一边匹配前面能匹配的哈希值。注意“CCF 喜欢大的”——ben090302,所以模数最好取大点,最好自然溢出。时间复杂度 \(O(n\log n)\)。代码异常简短,这就是分治吗(CDQ)。
code
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define int long long
using namespace std;
const int maxn=2e6+3;
const int base=73;
const int mod=729644203597;
int n;
int s[maxn];
int stk[maxn],top;
int ha[maxn];
__gnu_pbds::gp_hash_table<int,int>mp;
int calc(int l,int r){
top=0;
int mid=(l+r)>>1;
int res=0;
mp.clear();
for(int i=mid;i>=l;i--){
if(top&&stk[top]==s[i]){
top--;
}else{
stk[++top]=s[i];
ha[top]=(ha[top-1]*base%mod+stk[top])%mod;
}
mp[ha[top]]++;
}
top=0;
for(int i=mid+1;i<=r;i++){
if(top&&stk[top]==s[i]){
top--;
}else{
stk[++top]=s[i];
ha[top]=(ha[top-1]*base%mod+stk[top])%mod;
}
if(mp.find(ha[top])!=mp.end())res+=mp[ha[top]];
}
top=0;
return res;
}
int dfs(int l,int r){
if(r==l) return 0;
if(r-l+1==2) return s[l]==s[r];
int mid=(l+r)>>1;
return calc(l,r)+dfs(l,mid)+dfs(mid+1,r);
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
char ch;
cin>>ch;
s[i]=ch-'a'+1;
}
cout<<dfs(1,n);
return 0;
}
P8819 [CSP-S 2022] 星战
给你 \(n\) 个点 \(m\) 条边的有向图,\(q\) 次操作,有以下 4 种:
- 将边 \(u\to v\) 设为 disabled;
- 将以 \(u\) 为终点的边全部设为 disabled;
- 将边 \(u\to v\) 设为 enabled;
- 将以 \(u\) 为终点的边全部设为 enabled。
对于每次操作后,判断是否所有点是否只有一个 enabled 的出边且可以走 enabled 边到达一个 enabled 的环。
\(n,m\le 5\times 10^5\)
不可以总司令可以说明很难合法。先考虑出度为 1 的限制,这个可以哈希搞,具体就是先随机赋权 \(w(u)\),设 \(in(u)\) 表示以点 \(u\) 为终点的边的起点 \(v\) 的权值和。1,3 操作直接修改即可,2,4操作记一个初始 \(in\),每次修改直接减去差值即可,每次判断当前哈希值与所有出边为 1 的哈希值是否相等。
注意到现在就 A 了。
???(其实很显然如果出度全为 1 一定可以一直走下去,脑抽了)
时间复杂度 \(O(m+q)\)。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5e5+3;
const int p1=1370531;
const int p2=3563753;
const int mod1=1142905781;
const int mod2=993244853;
int n,m,q;
int out1[maxn],out2[maxn],ou1[maxn],ou2[maxn];
int ha1,ha2,th1,th2,bas1[maxn],bas2[maxn];
vector<int>e[maxn];
void del(int u,int v){
ha1=(ha1-bas1[u]+mod1)%mod1;
ha2=(ha2-bas2[u]+mod2)%mod2;
out1[v]=(out1[v]-bas1[u]+mod1)%mod1;
out2[v]=(out2[v]-bas2[u]+mod2)%mod2;
}
void delall(int u){
ha1=(ha1-out1[u]+mod1)%mod1;
ha2=(ha2-out2[u]+mod2)%mod2;
out1[u]=0;
out2[u]=0;
}
void add(int u,int v){
ha1=(ha1+bas1[u])%mod1;
ha2=(ha2+bas2[u])%mod2;
out1[v]=(out1[v]+bas1[u])%mod1;
out2[v]=(out2[v]+bas2[u])%mod2;
}
void addall(int u){
ha1=(ha1+(ou1[u]-out1[u]+mod1)%mod1)%mod1;
ha2=(ha2+(ou2[u]-out2[u]+mod2)%mod2)%mod2;
out1[u]=ou1[u];
out2[u]=ou2[u];
}
bool calc(){
return ha1==th1&&ha2==th2;
}
signed main(){
ios::sync_with_stdio(0);
cin>>n>>m;
bas1[0]=bas2[0]=1;
for(int i=1;i<=n;i++){
bas1[i]=bas1[i-1]*p1%mod1;
bas2[i]=bas2[i-1]*p2%mod2;
th1=(th1+bas1[i])%mod1;
th2=(th2+bas2[i])%mod2;
}
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;
out1[v]=(out1[v]+bas1[u])%mod1;
ou1[v]=(ou1[v]+bas1[u])%mod1;
out2[v]=(out2[v]+bas2[u])%mod2;
ou2[v]=(ou2[v]+bas2[u])%mod2;
ha1=(ha1+bas1[u])%mod1;
ha2=(ha2+bas2[u])%mod2;
}
cin>>q;
while(q--){
int op,a,b;
cin>>op;
if(op==1){
cin>>a>>b;
del(a,b);
}else if(op==2){
cin>>a;
delall(a);
}else if(op==3){
cin>>a>>b;
add(a,b);
}else{
cin>>a;
addall(a);
}
if(calc()){
cout<<"YES\n";
}else{
cout<<"NO\n";
}
}
return 0;
}
P9870 [NOIP2023] 双序列拓展
给你两个序列 \(a_n,b_m\),询问是否存在一组 \(a,b\) 的扩展 \(A_L,B_L(L=+\infty)\) 满足 \(\forall(A_i-B_i)(A_j-B_j)>0,i,j\in[1,+\infty)\)。
\(q\) 次修改 \(a,b\) 中的一些数。
\(a_n\) 的扩展 \(A_L\) 定义为存在一个序列 \(t=\{t_1,t_2,\cdots,t_n\}\) 满足 \(\sum t_i=L\) 且 \(A_L=\{a_1\times t_1+a_2\times t_2+\cdots+a_n\times t_n\}\),其中 \(+,\times\) 表示拼接与重复。
\(n,m\le 5\times 10^5,q\le 50\)
逆天 Ad-hoc。建议 FTR 11/AT 15.9。
与扩展长度相关的算法显然先要枪毙。然后就不会了。
不是哥们。
考虑序列中的数的种类至多 \(n+m\) 个,考虑设计相关复杂度的算法。
\(\forall(A_i-B_i)(A_j-B_j)>0\) 唉这个我认识。即要满足 \(\forall i,A_i<B_i\) 或 \(\forall i,A_i>B_i\),这两种本质一样。先考虑第一种。
考虑到我们只有边界条件要关心,其余的位置便自动合法。当扩展长度 \(L\to L+1\) 时,设当前 \(a\) 使用的是 \(a_i\) 扩展,\(b\) 使用的是 \(b_j\) 扩展,则只会出现四种情况:
- \((i,j)\to(i,j)\):还是使用原先的两个,所以不是边界,不用管;
- \((i,j)\to(i+1,j)\):\(a\) 变为使用 \(a_{i+1}\),这种情况需要满足 \(a_{i+1}<b_j\) 才能转移;
- \((i,j)\to(i,j+1)\):\(b\) 变为使用 \(b_{j+1}\),这种情况需要满足 \(a_{i}<b_{j+1}\) 才能转移;
- \((i,j)\to(i+1,j+1)\):\(a\) 变为使用 \(a_{i+1}\),\(b\) 变为使用 \(b_{j+1}\),这种情况需要满足 \(a_{i+1}<b_{j+1}\) 才能转移;
转移都出来了,直接 DP 是 \(O(qnm)\) 的,可以得 35 分。设 \(f(i,j)\) 表示当前 \(a\) 使用的是 \(a_i\) 扩展,\(b\) 使用的是 \(b_j\) 扩展是否合法,则有:
很不能优化的样子。考虑上面在干啥。
相当于有一个矩阵 \(c_{i,j}=[a_i<b_j]\) 要从 \((1,1)\) 走到 \((n,m)\) 可以向右、下、右下走,且要满足 \(c_{i,j}=1\)。
考虑什么时候无解。当 \(a_{\min}\ge b_{\min}/b_{\max}\le a_{\max}\) 时 \(b_{\min}/a_{\max}\) 那一列/排的 \(c\) 都为 0,所以无解。
特殊性质:\(a_n \ll a_1<b_1\ll b_m\),所以有 \(a_n< \forall b_j,b_m>\forall a_i\)。即最后一行/列都是 1。所以我们只要到达最后一行/列即可。
我们只要走到 \(n-1\) 行或 \(m-1\) 列就到了。递归地看,问题变小。然后其实变成了上面的子问题。直接递归求解即可(合法情况下,即 \(\exists a_i=a_{\min}<\forall b_j=b_{\min}/\exists b_j=b_{\max}>\forall a_i=a_{\max}\),说明我们只要走到 \(i\) 行/\(j\) 列即可,再次缩小范围求解)。
没有特殊性质也一样。仅第一步不同而已。预处理前/后缀 \(\min/\max\),时间复杂度 \(O(q(n+m))\)。
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+7;
int a[maxn],b[maxn],f[maxn],g[maxn],ta[maxn],tb[maxn];
#define get(A,p) {A[i]>A[p.mx] ? i : p.mx, A[i]<A[p.mi] ? i : p.mi}
struct node{
int mx,mi;
node(int x=0,int y=0): mx(x),mi(y){}
}pren[maxn],prem[maxn],sufn[maxn],sufm[maxn];
void update(int n,int m){
pren[1]={1,1}; sufn[n]={n,n};
prem[1]={1,1}; sufm[m]={m,m};
for(int i=2;i<=n;i++) pren[i]=get(f,pren[i-1]);
for(int i=2;i<=m;i++) prem[i]=get(g,prem[i-1]);
for(int i=n-1;i ;i--) sufn[i]=get(f,sufn[i+1]);
for(int i=m-1;i ;i--) sufm[i]=get(g,sufm[i+1]);
}
bool check1(int x,int y,int n,int m){
if(x==1||y==1) return 1;
node X=pren[x-1],Y=prem[y-1];
if(f[X.mi]<g[Y.mi]) return check1(X.mi,y,n,m);
if(f[X.mx]<g[Y.mx]) return check1(x,Y.mx,n,m);
return 0;
}
bool check2(int x,int y,int n,int m){
if(x==n||y==m) return 1;
node X=sufn[x+1],Y=sufm[y+1];
if(f[X.mi]<g[Y.mi]) return check2(X.mi,y,n,m);
if(f[X.mx]<g[Y.mx]) return check2(x,Y.mx,n,m);
return 0;
}
bool solve(int tta[],int ttb[],int n,int m){
if(tta[1]>=ttb[1]) return 0;
for(int i=1;i<=n;i++) f[i]=tta[i];
for(int i=1;i<=m;i++) g[i]=ttb[i];
update(n,m);
node X=pren[n],Y=prem[m];
if(f[X.mi]>=g[Y.mi] || f[X.mx]>=g[Y.mx]) return 0;
return check1(X.mi,Y.mx,n,m) && check2(X.mi,Y.mx,n,m);
}
signed main(){
int c,n,m;
cin>>c>>n>>m;
int T;
cin>>T;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
cin>>b[i];
}
cout<<"01"[solve(a,b,n,m)||solve(b,a,m,n)];
while(T--){
for(int i=1;i<=n;i++) ta[i]=a[i];
for(int i=1;i<=m;i++) tb[i]=b[i];
int k1,k2;
cin>>k1>>k2;
for(int i=1,x,y;i<=k1;i++){
cin>>x>>y;
ta[x]=y;
}
for(int i=1,x,y;i<=k2;i++){
cin>>x>>y;
tb[x]=y;
}
cout<<"01"[solve(ta,tb,n,m)||solve(tb,ta,m,n)];
}
return 0;
}