Description
给出一张 n n 个点的有向图,点的编号为,若 0≤i<j<n 0 ≤ i < j < n ,则 i i 到之间有一条权值为 i i ^的边,问 0 0 点到点的最大流
Input
多组用例,每组用例输入一整数 n(1≤n≤1018) n ( 1 ≤ n ≤ 10 18 )
Output
输出 0 0 到的最大流,结果模 109+7 10 9 + 7
Sample Input
2
Sample Output
1
Solution
设 n−1 n − 1 的二进制最高位为 2k 2 k ,那么对于 1≤i<2k 1 ≤ i < 2 k 有 i<2k≤i i < 2 k ≤ i ^ n−1 n − 1 ,即从 0 0 到 i i 的流都可以从 i i 流到 n−1 n − 1 ,这部分对答案的贡献为 ∑i=12k−1i=2k−1⋅(2k−1) ∑ i = 1 2 k − 1 i = 2 k − 1 ⋅ ( 2 k − 1 ) ,对于 2k≤i<n 2 k ≤ i < n 有 i≥2k>i i ≥ 2 k > i ^ n−1 n − 1 ,即从 0 0 到 i i 的流会把 i i 到 n−1 n − 1 的边流满,剩下的流也没办法到达 n−1 n − 1 了(只有较小编号的 i i 到 n−1 n − 1 的边还没满流,但是边的编号都是从小指向大,所以没办法把这部分没有流满的流通过较小编号的点流向 n−1 n − 1 ),这部分答案为 ∑i=2kn−1i ∑ i = 2 k n − 1 i ^ n−1 n − 1 ,令 m=n−1−2k m = n − 1 − 2 k ,则该式即为 ∑i=0mi ∑ i = 0 m i ^ m m ,从高到低按二进制位考虑贡献,对于第 i i 位,假设对于高位小于 m m 的数字已经累加贡献,只考虑高位和 m m 的高位相同的数字,如果 m m 在第 i i 位是 0 0 那么构造的数这位也只能是 0 0 且对答案没有贡献,否则在该位取 0 0 对答案会有贡献,贡献分为两部分,首先第 i i 位取 0 0 ,第 0 0 位到第 i−1 i − 1 位随便取,有 2i 2 i 个数字,这些数字与 m m 异或在第 i i 位均为 1 1 ,贡献为 2i⋅2i=22i 2 i ⋅ 2 i = 2 2 i ,另一部分贡献为当第 i i 位取 0 0 时,较低位随便取也会在低位和 n n 异或产生贡献,对于第 j(0≤j≤i−1) j ( 0 ≤ j ≤ i − 1 ) 位,不管 n n 在该位 0 0 或 1 1 ,这 2i 2 i 个数字中必有 2i−1 2 i − 1 个在该位与 n n 相同,另外 2i−1 2 i − 1 个和 n n 不同,贡献为 2i−1⋅2j 2 i − 1 ⋅ 2 j ,总贡献即为 22i+∑j=0i−12i−1⋅2j=22i+2i−1⋅(2i−1) 2 2 i + ∑ j = 0 i − 1 2 i − 1 ⋅ 2 j = 2 2 i + 2 i − 1 ⋅ ( 2 i − 1 )
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=202;
#define mod 1000000007
int f[maxn];
int main()
{
f[0]=1;
for(int i=1;i<=200;i++)f[i]=2*f[i-1]%mod;
ll n;
while(~scanf("%lld",&n))
{
int ans=0;
n--;
for(int i=60,first=1;i>=0;i--)
if((n>>i)&1)
{
if(first)ans=(ans+(ll)f[i-1]*(f[i]-1)%mod)%mod,first=0;
else ans=((ans+f[2*i])%mod+(ll)f[i-1]*(f[i]-1)%mod)%mod;
}
ans=(ans+n)%mod;
printf("%d\n",ans);
}
return 0;
}