题目链接;
由于符号等缘故,题面在此不再粘贴,想看题面的可以点击上方链接或windows左右分屏观看;
1.题外话
看到∑,就恶心;
看到黄题,就想切;
总之,心情很复杂,硬着头皮看下去,发现没这么难
2.解题意
外层循环,从1~i;
内层循环,从1~j;
从第二层循环考虑,易知对于每一个ai,都会和位于其之前的aj进行一次异或并且加和。并且当i增大后,位于当前这个ai后面的a元素也会与其进行异或并累加;
也就是说,我们可以这样理解:每一个ai都与序列l中剩下的a进行了一次异或并且累加。
这是对于一个ai;将外层循环考虑在内,我们就知道:题目让求的就是所有的元素与其他任意一个元素的异或之和。(类似于两两握手那种问题。只不过是把握手换成了异或)。
3.找思路
由数据得,a最大为10^12.因为是异或,那么二进制表示下各个位数之间互不干扰。并且题目只是给了n,要使∑值最大,很容易便想到每一个a都需要达到位数上限n。既然位数相同,题目又没有给你具体每一个a的数值,我们便可以一位一位考虑,每一位取到和的最大值,再*2^k进行累加即可。
求答案的方法已经基本成型了,剩下的就是确定每一位异或和最大的情况。
对一位进行考虑:
两两异或,其中一个为1,另一个为0,则答案为1。
我们设有x个数为0。
对于每一个为1的数,都可以和所有为0的数异或得1,对答案的贡献就是x;
总个数为l,则剩下的大小为1的数有l-x个,一个就可以为答案增加x,l-x个就可以为答案增加x*(l-x)。很明显,当x取中间值l/2时上式取值最大.(具体为什么参考“和同近积大”原理或者二次函数求极值即可)
于是,我们求得了每一位对答案最大的贡献,接着把每一位对答案的贡献累加起来,就是题目所求答案。
4.代码实现
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #define ll long long 6 #define re register 7 #define modd 1000000007 8 using namespace std; 9 inline int read() 10 { 11 int x=0,f=1; char ch=getchar(); 12 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 13 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 14 return x*f; 15 } 16 int t; 17 long long n,l,ans; 18 int main() 19 { 20 t=read(); 21 while(t--) 22 { 23 ans=0; 24 scanf("%lld%lld",&n,&l); 25 long long mid=l>>1;//求最大值所用 26 if(n==1)//特判、因没有而WA掉。简单思考一下就知道了 27 { 28 printf("0 "); 29 continue; 30 } 31 long long big=1ll<<40;//10^12到不了40位 32 while(big) 33 { 34 big>>=1; 35 if(n<big)continue;//如果没到n的最大位,就位数-- 36 ans+=big*mid*(l-mid);//累加答案 37 ans%=modd;//不取模见祖宗 38 } 39 printf("%lld ",ans); 40 } 41 return 0; 42 }
完结撒花。