trichlorotrifluoroethane

P9754 [CSP-S 2023] 结构体

一年的痛终于解决。

一个结构体的对齐要求为其成员的对齐要求的 \(\gcd\),其大小为大于等于实际大小的最小整除对齐要求的数,基础类型的对齐要求为其大小。

给你一个无限长的内存,头地址为 \(0\),支持以下操作:

  1. X k t1 n1...tk nk 声明一个结构体名字为 \(X\) 包含 \(k\) 个成员,第 \(i\) 个成员的类型为 \(t_i\),名字为 \(n_i\)。输出结构体大小和对齐要求;
  2. t n 定义一个变量名字为 \(n\),类型为 \(t\)。输出其开始地址;
  3. s 求变量 \(s\) 的开始地址;
  4. 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\) 次操作:

  1. + a b \(x_a\leftarrow x_b\)
  2. - a b \(x_a\leftarrow \neg x_b\)
  3. 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,则有转移

\[f(i,t)=\min(f(i+1,t-1)+10^{t-1}\cdot n_i,f(i+1,t)+c_{n_i}) \]

时间复杂度 \(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\) 扩展是否合法,则有:

\[f(i,j)=[a_i<b_j]\And(f(i-1,j)\mid f(i,j-1)\mid f(i-1,j-1)) \]

很不能优化的样子。考虑上面在干啥。

相当于有一个矩阵 \(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;
}
posted @ 2024-10-04 21:34  view3937  阅读(11)  评论(0编辑  收藏  举报
Title