NKOJ-8829 密码门

问题描述:

为了防止秘密情报泄露,Kiana决定在基地内加装n道密码门,每道密码门可以对输入的数字做一定计算,并将结果输出给下一道密码门作为输入。
只要Kiana对比入口处输入的数字经过所有密码门后的结果,就可以判断出来者是否掌握真正的密码。 具体而言,第 i 道密码门有一个操作符 OPi 和一个参数 Bi,其中操作符分为三种:AND、OR和XOR,分别表示该密码门会将输入的数字与 Bi 进行按位与、按位或、按位异或后进行输出。
按位与对应C中的 ′
&′ 运算符,按位或对应C中的 ′|′ 运算符,按位异或对应C++中的 ′\^′ 运算符,你可以分别用表达式 ′a&b′、′a|b′ 和 ′a\^b′ 计算两个数a和b进行按位与、按位或、按位异或运算的结果。 为进一步保证安全,在某些时刻,Kiana还可能修改某些密码门的操作符和参数。
基地中按时间顺序共发生了m个事件,每个事件可能为有人在基地门口输入了一个数字,或者Kiana修改了某一道密码门的操作符和参数。 对于每个输入数字事件,虽然密码门可以自动计算出结果来,但Kiana还是希望提前知道该数字依次通过所有密码门后的结果是什么,以便针对特殊情况做出预警。由于Kiana自己不会算,所以希望你能够帮助她。

输入格式:

第一行包含两个正整数n和m,分别表示密码门的数量与发生的事件数。

接下来n行,第i行包含一个字符串OPi和一个正整数Bi,分别表示初始时第i道密码门的操作符和参数。

接下来m行,每行首先包含一个正整数type,若type=1,则后面跟一个正整数X,表示有人在门口输入了数字X,你需要计算X依次通过所有密码门后的结果;若type=2,则后面跟一个正整数id、一个字符串OP′和一个正整数B′,表示Kiana将第id道密码门的操作符改为了OP′,参数改为了B′。

数据保证输入中的字符串均为'AND''OR''XOR'中的一种,其分别对应这道密码门进行按位与、按位或、按位异或运算。

输出格式:

对于每个输入数字事件,输出一行一个正整数,表示基地门口输入的数字依次通过所有密码门后输出的结果。

数据范围:

对于20的数据,保证1≤n,m≤2000。

对于60的数据,保证1≤n,m≤20000。

对于100的数据,保证1≤n,m≤2×10e5,1≤type≤2,1≤Bi,B′,X≤1000,1≤id≤n。

在后两档数据中,各有三组数据没有发生过Kiana修改密码门的事件,好各有三组数据所有的操作符都是相同的.

 

 

  比较板的一道题。

  布吉岛为什么当时脑残了,没有做出来,qwq。

 

  首先,如果只有一种运算是很好做的,我们只需要按位记一下就可以了,但三种在一起怎么办?

  (看题解可知)区间考虑线段树,维护 全0进入的返回值 和 全1进入的返回值 + 单点修改。(详见代码)

 

Code:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
string IP;
int n,m,S,A[200005],B[200005];
struct node {int L,R,opt,V0,V1;}Tr[1600005];
inline void UpData(int x)
{
    Tr[x].V0=Tr[x].V1=0;
    for(register int i=0;i<10;++i)
    {
        if(Tr[x<<1].V0>>i&1) Tr[x].V0|=((Tr[x<<1|1].V1>>i&1)<<i);
        else Tr[x].V0|=((Tr[x<<1|1].V0>>i&1)<<i);
        if(Tr[x<<1].V1>>i&1) Tr[x].V1|=((Tr[x<<1|1].V1>>i&1)<<i);
        else Tr[x].V1|=((Tr[x<<1|1].V0>>i&1)<<i);
    }
}
inline void Build(int x,int L,int R)
{
    Tr[x].L=L,Tr[x].R=R;
    if(L==R)
    {
        Tr[x].opt=A[L];
        switch(Tr[x].opt)
        {
            case 1:{Tr[x].V0=0^B[L],Tr[x].V1=S^B[L];break;}
            case 2:{Tr[x].V0=0&B[L],Tr[x].V1=S&B[L];break;}
            case 3:{Tr[x].V0=0|B[L],Tr[x].V1=S|B[L];break;}
        }
        return;
    }
    int M=(L+R)>>1;Build(x<<1,L,M),Build(x<<1|1,M+1,R),UpData(x);
}
inline void Modify(int x,int pos,int opt,int V)
{
    if(Tr[x].L==Tr[x].R)
    {
        Tr[x].opt=opt;
        switch(Tr[x].opt)
        {
            case 1:{Tr[x].V0=0^V,Tr[x].V1=S^V;break;}
            case 2:{Tr[x].V0=0&V,Tr[x].V1=S&V;break;}
            case 3:{Tr[x].V0=0|V,Tr[x].V1=S|V;break;}
        }
        return;
    }
    if(pos<=Tr[x<<1].R) Modify(x<<1,pos,opt,V);
    else Modify(x<<1|1,pos,opt,V);
    UpData(x);
}
inline int GetAns(int x)
{
    int RET(0);
    for(register int i=0;i<10;++i)
    {
        if(x>>i&1) RET|=((Tr[1].V1>>i&1)<<i);
        else RET|=((Tr[1].V0>>i&1)<<i);
    }
    return RET;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m,S=(1<<10)-1;
    for(register int i=1;i<=n;++i)
    {
        cin>>IP>>B[i];
        switch(IP[0]) {case 'X':{A[i]=1;break;}case 'A':{A[i]=2;break;}case 'O':{A[i]=3;break;}}
    }
    Build(1,1,n);
    for(register int i=1,x,y,z;i<=m;++i)
    {
        cin>>x;
        if(x==1) cin>>y,printf("%d\n",GetAns(y));
        else
        {
            cin>>x>>IP>>z;
            switch(IP[0]) {case 'X':{y=1;break;}case 'A':{y=2;break;}case 'O':{y=3;break;}}
            Modify(1,x,y,z);
        }
    }
    return 0;
}
密码门

 

posted @ 2021-11-10 19:18  qfxl  阅读(276)  评论(0)    收藏  举报