51nod1674:区间的价值2(分治,利用&和|的收敛性)

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

原文地址:https://www.cnblogs.com/hua-dong/p/9185351.html

时间: 2024-10-10 10:35:22

51nod1674:区间的价值2(分治,利用&和|的收敛性)的相关文章

1674 区间的价值 V2(分治)

1674 区间的价值 V2 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 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,1],

[hdu] 5696 区间的价值 || 序列分治

原题 我们定义"区间的价值"为一段区间的最大值*最小值. 一个区间左端点在L,右端点在R,那么该区间的长度为(R?L+1). 求长度分别为1-n的区间的最大价值. 保证数据随机 因为保证数据随机,所以我们可以考虑用区间的最大值把这个区间分为两个部分,这样答案的贡献就有两种情况. 1.在同一个区间里 2.跨过最大值,在两个区间里 情况1通过递归就变成了情况2,而情况二我们通过two-points来完成.记录l指针和r指针,因为所求为最大值,所以选取l和r指针较大的内个加入,并每次更新答案

hdu 5696 区间的价值 单调栈+rmq

区间的价值 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Description 我们定义“区间的价值”为一段区间的最大值*最小值. 一个区间左端点在L,右端点在R,那么该区间的长度为(R−L+1). 现在聪明的杰西想要知道,对于长度为k的区间,最大价值的区间价值是多少. 当然,由于这个问题过于简单. 我们肯定得加强一下. 我们想要知道的是,对于长度为1∼n

区间的价值(线段树)百度之星

区间的价值 Accepts: 0 Submissions: 0 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Problem Description 我们定义“区间的价值”为一段区间的最大值*最小值. 一个区间左端点在LLL,右端点在RRR,那么该区间的长度为(R−L+1)(R-L+1)(R−L+1). 现在聪明的杰西想要知道,对于长度为kkk的区间,最大价值的区间价值是多少.

HDU5696 区间的价值

传送门 一道比较基础的分治题--但是我似乎不会分治-- 首先,区间的价值肯定与最小值有关,所以对于当前处理的区间,我们首先暴力的求出区间的最小值,其位置记为pos.之后--对于一个横跨过pos的区间,它的价值必定可以由pos左右两边区间的价值更新而来,所以说我们只需要暴力的在左右两边O(n)的扫一遍求出相应的长度的区间价值最大值,之后用较短的区间的价值更新较长的即可. 之后再递归分治pos的左右两边的区间即可. 本题的关键在于,能通过找到pos这个位置,之后把横跨过pos的区间的值由左右两边更新

51nod 1674 区间的价值V2(思维+拆位+尺取法)

最近被四区题暴虐... 题意:lyk拥有一个区间. 它规定一个区间的价值为这个区间中所有数and起来的值与这个区间所有数or起来的值的乘积. 例如3个数2,3,6.它们and起来的值为2,or起来的值为7,这个区间对答案的贡献为2*7=14. 现在lyk有一个n个数的序列,它想知道所有n*(n+1)/2个区间的贡献的和对1000000007取模后的结果是多少. 区间的and值和区间的or值相乘,实际上等于将and值分解为2的幂次和的形式与or值分解成2的幂次和的形式相乘. 所以对于同一段区间来说

HDU 5696 ——区间的价值——————【线段树、快排思想】

区间的价值 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 662    Accepted Submission(s): 329 Problem Description 我们定义“区间的价值”为一段区间的最大值*最小值. 一个区间左端点在L,右端点在R,那么该区间的长度为(R−L+1). 现在聪明的杰西想要知道,对于长度为k的区间,最大

hdu5696区间的价值 -- 2016&quot;百度之星&quot; - 初赛(Astar Round2B)

Problem Description 我们定义“区间的价值”为一段区间的最大值*最小值. 一个区间左端点在L,右端点在R,那么该区间的长度为(R−L+1). 现在聪明的杰西想要知道,对于长度为k的区间,最大价值的区间价值是多少. 当然,由于这个问题过于简单. 我们肯定得加强一下. 我们想要知道的是,对于长度为1∼n的区间,最大价值的区间价值分别是多少. 样例解释: 长度为1的最优区间为2−2 答案为6∗6 长度为2的最优区间为4−5 答案为4∗4 长度为3的最优区间为2−4 答案为2∗6 长度

ACM学习历程—HDU5696 区间的价值(分治 &amp;&amp; RMQ &amp;&amp; 线段树 &amp;&amp; 动态规划)

http://acm.hdu.edu.cn/showproblem.php?pid=5696 这是这次百度之星初赛2B的第一题,但是由于正好打省赛,于是便错过了.加上2A的时候差了一题,当时有思路,但是代码就是过不去..这次应该是无缘复赛了.. 先不水了,省赛回来,我看了一下这个题,当时有个类似于快排的想法,今天试了一下,勉强AC了..跑了3S多. 思路就是我枚举区间左值lt,那么[lt, n]区间内最值的角标分别为mi和ma.于是设to = max(mi, ma).也就是说在to右侧的所有区间