题目:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=13&page=show_contest&contest=350
题意:有N盏灯和M个开关,每一个开关控制多盏灯(比如N=4,"0011"就代表这个开关控制第3和第4盏灯),现在问你有多少种按开关的方法使得所有的灯都熄灭(每个开关只能按一次,并且按的开关的编号是连续的),另外所按的编号连续的开关的长度在[a,b]范围内。初始所有的灯都是开着的。
分析:比赛到最后20分钟的时候想出来了,没写出来,时间不够。。。。。首先可以知道选择的区间的异或和必须为全1,这样就保证了每一盏灯的状态被改变了奇数次。然后维护前缀异或和xor[1,i],找一个在这之前的前缀异或和xor[1,j](j<i),使得xor[1,i]^xor[1,j]为全1状态,那么[j+1,i]就是一个所求区间。怎么找到所有的xor[i,j]?用一个map即可,前缀异或和当key,所有等于key的前缀异或和的尾位置当值。然后用二分找到所有满足条件的区间就行。(另外:由于N<=50,01串可以直接转换成long long类型)
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <map> #include <vector> using namespace std; typedef long long LL; int L,N,A,B; char str[80]; map <LL ,vector <int > > mp; LL toll(char str[]) { LL ret=0,p=1; for(int i=L-1;i>=0;i--) { if(str[i]=='1') ret=ret+p; p<<=1; } return ret; } int cnt(LL x,int pos) { vector <int > &vct=mp[x]; int down=0,mid,up=vct.size()-1,fd=-1; while(down<=up) { mid=(down+up)>>1; if(pos-vct[mid]<A) up=mid-1; else { down=mid+1; if(mid>fd) fd=mid; } } down=0; up=vct.size()-1; int fd1=up+1; while(down<=up) { mid=(down+up)>>1; if(pos-vct[mid]>B) down=mid+1; else { up=mid-1; if(mid<fd1) fd1=mid; } } if(fd1>fd) return 0; return fd-fd1+1; } int main() { int i,j,ncase=1; while(scanf("%d%d%d%d",&L,&N,&A,&B)!=EOF) { mp.clear(); for(i=0;i<L;i++) str[i]='1'; str[i]='\0'; LL f=toll(str),ans=0,x,xorsum=0; for(i=1;i<=N;i++) { scanf("%s",str); x=toll(str); xorsum^=x; if(xorsum==f && A<=i && i<=B) ans++; ans+=cnt(f^xorsum,i); mp[xorsum].push_back(i); } printf("Case %d: %lld\n",ncase++,ans); } return 0; }
uva Internet of Lights and Switches (异或运算)
原文地址:http://blog.csdn.net/w20810/article/details/48138143