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; }