codeforces 1030E (暴力+思維)
阿新 • • 發佈:2018-12-13
題意:
給定一些數,可將區間 l ~r 中某些數的二進位制位的1的位置更換, 使得最終區間所有數異或和為0,求這樣的區間個數。
思路:
在那裡瞎dp了好久,wa的很徹底,借鑑了一下別人的思路。
區間合法的條件是:
這個區間1的個數為偶數,並且區間中二進位制位1最多的一個數的二進位制個數小於等於和的一半。
我們發現一個數至少可以增加一個二進位制位,1e18,大約在2^61次方。那麼區間長度大於63時,一定可以把數字相互抵消成0,(即第二個條件一定滿足)。所以我們只需統計有多少個偶數區間即可。
對於區間長度小於63的,我們直接暴力列舉即可。
程式碼如下:
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <string> #include <vector> #include <map> #include <queue> using namespace std; const int maxn = 3e5+100; long long a[maxn]; long long get(long long n) { long long num = 0; while(n>0) { if(n%2!=0) num++; n/=2; } return num; } long long numj[maxn]; long long numo[maxn]; long long all[maxn]; int main() { int n; scanf("%d",&n); for(int i=1; i<=n; i++) scanf("%lld",&a[i]); long long ans = 0; all[0] = 0; numo[0] = 1; for(int i=1; i<=n; i++) { a[i] = get(a[i]); //剪枝操作 all[i] = all[i-1] + a[i]; long long mx = a[i]; for(int j=i-1; j>=max(1,i-62); j--) { mx = max(mx,a[j]); //如果每次求一個get(a[j]) 會TE,所以直接儲存就好 long long len = all[i] - all[j-1]; if((len%2==0) && (len>=mx*2)) ans++; } if(i>63) { if(all[i]&1) ans+= numj[i-64]; else ans+= numo[i-64]; } numj[i] = numj[i-1] + (all[i]%2==1); numo[i] = numo[i-1] + (all[i]%2==0); } printf("%lld\n",ans); return 0; }