1. 程式人生 > >51nod1674:區間的價值2(分治,利用&和|的收斂性)

51nod1674:區間的價值2(分治,利用&和|的收斂性)

return sam 一行 data- ID 所有 style 運算 iterator

lyk擁有一個區間。

它規定一個區間的價值為這個區間中所有數and起來的值與這個區間所有數or起來的值的乘積。 例如3個數2,3,6。它們and起來的值為2,or起來的值為7,這個區間對答案的貢獻為2*7=14。 現在lyk有一個n個數的序列,它想知道所有n*(n+1)/2個區間的貢獻的和對1000000007取模後的結果是多少。 例如當這個序列為{3,4,5}時,那麽區間1,11,1,1,21,2,1,31,3,2,22,2,2,32,3,3,33,3的貢獻分別為9,0,0,16,20,25。

Input第一行一個數n(1<=n<=100000)。
接下來一行n個數ai,表示這n個數(0<=ai<=10^9)。Output一行表示答案。Sample Input

3
3 4 5

Sample Output

70

題意:求所有區間的&值乘|值。

思路:分治,利用這兩個邏輯運算的收斂性,用map記錄相應個數,然後累加即可。

(和前面利用gcd的收斂性分治的一樣的。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100010;
const int Mod=1000000007;
ll ans;
ll a[maxn],And[maxn],Or[maxn]; 
map<pair<ll,ll>,ll>mp;
map
<pair<ll,ll>,ll>::iterator it; void solve(int L,int R) { if(L>R) return ; int Mid=(L+R)>>1; mp.clear(); And[Mid]=a[Mid]; Or[Mid]=a[Mid]; mp[make_pair(And[Mid],Or[Mid])]++; for(int i=Mid-1;i>=L;i--){ And[i]=And[i+1]&a[i]; Or[i]=Or[i+1
]|a[i]; mp[make_pair(And[i],Or[i])]++; } for(int i=Mid;i<=R;i++){ for(it=mp.begin();it!=mp.end();it++) ans+=(ll)(And[i]&(*it).first.first)*(Or[i]|(*it).first.second)%Mod*(*it).second%Mod; } solve(L,Mid-1); solve(Mid+1,R); } int main() { int N,i,j; scanf("%d",&N); for(i=1;i<=N;i++) scanf("%lld",&a[i]); solve(1,N); printf("%lld\n",ans); return 0; }

小小優化:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100010;
const int Mod=1e9+7;
int a[maxn],And[maxn],Or[maxn]; ll ans;
map<pair<int,int>,int>mp,tp;
map<pair<int,int>,int>::iterator it1,it2;
void solve(int L,int R)
{
    if(L>R) return ;
    int Mid=(L+R)>>1; 
    mp.clear(); tp.clear();
    And[Mid]=a[Mid]; Or[Mid]=a[Mid];
    mp[make_pair(And[Mid],Or[Mid])]++;
    for(int i=Mid-1;i>=L;i--){
        And[i]=And[i+1]&a[i];
        Or[i]=Or[i+1]|a[i];
        mp[make_pair(And[i],Or[i])]++;
    }
    tp[make_pair(And[Mid],Or[Mid])]++;
    for(int i=Mid+1;i<=R;i++){
        And[i]=And[i-1]&a[i]; Or[i]=Or[i-1]|a[i];
        tp[make_pair(And[i],Or[i])]++;
    }
    for(it1=mp.begin();it1!=mp.end();it1++)
     for(it2=tp.begin();it2!=tp.end();it2++)
      ans=(ans+(ll)((*it1).first.first&(*it2).first.first)*((*it1).first.second|(*it2).first.second)%Mod*(*it1).second*(*it2).second)%Mod;
    solve(L,Mid-1); solve(Mid+1,R);
}
int main()
{
    int N,i,j;
    scanf("%d",&N);
    for(i=1;i<=N;i++) scanf("%d",&a[i]);
    solve(1,N);
    printf("%lld\n",ans);
    return 0;
}

51nod1674:區間的價值2(分治,利用&和|的收斂性)