一、比较简单的一道模拟题。对于两个数,他们有一个and值,一个or值,一个xor值。现在这三个值给你任意几个,让你求这两个数可能的方案数(注意:1,2和2,1是相同的方案),如果互相冲突就输出0,如果无穷解就输出inf,否则输出方案数。很容易发现这是一个枚举所有情况的模拟题,总共七种情况,一次判断是否冲突,是否无穷解,或者有解的个数。
无解的情况:(二进制下某一位)or为0,xor为1;xor为1,and为1;or为0,and为1
无穷解:只有一个xor、只有一个and
其他的都是有限解,就一次判断情况枚举就好了。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t,ans_and,ans_or,ans_xor;
ll ans;
int main(){
freopen("bit.in","r",stdin);
freopen("bit.out","w",stdout);
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&ans_and,&ans_or,&ans_xor);
if(ans_or==-1&&ans_xor==-1){
printf("inf\n");
}else if(ans_and==-1&&ans_xor==-1){
ans=1;
for(;ans_or;ans_or>>=1){
if(ans_or&1) ans*=3;
}
printf("%lld\n",ans);
}else if(ans_and==-1&&ans_or==-1){
printf("inf\n");
}else if(ans_xor==-1){
ans=1;bool flag=0;
for(int i=0;i<=31;i++){
if(ans_and&(1<<i)){
if(ans_or&(1<<i)) ans*=1;
else flag=1;
}
else{
if(ans_or&(1<<i)) ans*=2;
else ans*=1;
}
}
if(flag) printf("0\n");
else printf("%lld\n",ans);
}else if(ans_and==-1){
ans=1;bool flag=0;
for(int i=0;i<=31;i++){
if(ans_or&(1<<i)){
if(ans_xor&(1<<i)) ans*=2;
else ans*=1;
}
else{
if(ans_xor&(1<<i)) flag=1;
else ans*=1;
}
}
if(flag) printf("0\n");
else printf("%lld\n",ans);
}else if(ans_or==-1){
bool flag=0;ans=1;
for(int i=0;i<=31;i++){
if(ans_and&(1<<i)){
if(ans_xor&(1<<i)) flag=1;
}else{
if(ans_xor&(1<<i)) ans*=2;
}
}
if(flag) printf("0\n");
else printf("%lld\n",ans);
}else{
ans=1;bool flag=0;
for(int i=0;i<=31;i++){
if(ans_and&(1<<i)){
if(ans_or&(1<<i)) ans*=1;
else flag=1;
}
else{
if(ans_or&(1<<i)) ans*=2;
else ans*=1;
}
}
for(int i=0;i<=31;i++){
if(ans_and&(1<<i)){
if(ans_xor&(1<<i)) flag=1;
}
}
for(int i=0;i<=31;i++){
if(ans_or&(1<<i)==0){
if(ans_xor&(1<<i)) flag=1;
}
}
if(flag) printf("0\n");
else printf("%lld\n",ans);
}
}
return 0;
}
二、一道比较复杂的数组模拟题,简单来说就是你用一个数组维护一个集合A,然后判断集合中哪些数存在,你需要用桶维护,桶原来使用的bool数组你需要改成int,因为你的标记应该是一个时间戳。如果合并,就扫描B的所有数有没有在集合A中,没有就添加进数组里。如果是交集,先把A集合数组清零,仍然是扫描B,如果A中也有就加入新的集合。因为SUM B是3e6的,所以你复杂度O(SUM)完全可以过。当然中间还穿插有区间加和区间减,你只需要做一个懒标记就好了。我当时只拿了90,因为有一个区间加减的情况我还是去扫描A了所以复杂度不确定。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
char xch,xB[1<<15],*xS=xB,*xTT=xB;
#define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++)
inline int read() {
int x=0,f=1;char ch=getc();
while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
const int N=3e6+10;
const int M=1e6;
int m,a[N],b[N],f[N],top,past,de,siz;
ll sum;
int main(){
freopen("jihe.in","r",stdin);
freopen("jihe.out","w",stdout);
m=read();
past=1;
for(int i=1;i<=m;++i){
int op=read();
if(op==3){
sum+=top;++de;
printf("%lld\n",sum);
}else if(op==4){
sum-=top;--de;
printf("%lld\n",sum);
}else if(op==1){
for(int j=1;j<=top;++j){
f[a[j]+M]=0;
}
for(int j=1;j<=top;++j){
f[a[j]+de+M]=past;
a[j]=a[j]+de;
}
de=0;
siz=read();
for(int j=1;j<=siz;++j){
b[j]=read();
if(f[b[j]+M]!=past) a[++top]=b[j],f[b[j]+M]=past,sum+=b[j];
}
printf("%lld\n",sum);
}else if(op==2){
//for(int j=1;j<=top;++j) cout<<a[j]<<' ';
//cout<<endl;
for(int j=1;j<=top;++j){
f[a[j]+M]=0;
}
for(int j=1;j<=top;++j){
f[a[j]+de+M]=past;
}
de=0;
siz=read();top=0;sum=0;
for(int j=1;j<=siz;++j){
b[j]=read();
if(f[b[j]+M]==past)
a[++top]=b[j],f[b[j]+M]=i,sum+=b[j];
}
past=i;
printf("%lld\n",sum);
}
}
return 0;
}
第三题是一道容斥题,目前只会写70分的。把方块的四周都当成空地,如果没有空地,把相邻的两个相同方块的缝隙也当成空地。对所有的极大空地联通块儿编号,然后给方块标记。一个方块儿最多被四种编号的空地打标记,然后我们把每一种方块儿放到一个队列里,发现所有与某几种编号相邻的方块的个数可以做一个sum,然后从左往右扫一遍,做个容斥就好了。这中间记录前缀和会用到map,所以复杂度是N*M*15*logN。100分的并过不了。