1. 程式人生 > >2017 ACM-ICPC 亞洲區(西安賽區)網路賽 E Maximum Flow

2017 ACM-ICPC 亞洲區(西安賽區)網路賽 E Maximum Flow

題目連結

題意

n 個點,0,1,2,...,n1,對於沒對點 <i,j>(0i<j<n),有一條 ij 邊,流量為 iXorj,問從 0n1 的最大流。

思路

首先,0n1 必須要割;

其次,對於任意一個 i0iin1 至少要割一個。考慮 in1 的最高位是否為 0,如果為 0,就割 0i,否則割 in1.

這樣割,肯定有一個點為分界線(1000),前面的所有點與 n1 連通而不與 0 連通,後面的所有點與 0 連通而不與 n1 連通,於是 0n1 就割開了。

現在的問題就是怎麼求割下來的各條邊的流量和。

i

的最高位為 0 時,割 0i,就是 1+2+3+...+((1<<(len1))1).

i 的最高位為 1 時,割 in1,考慮從低位往高位去找 n1 中為 1 的位(除了最高位),不妨設當前為第 j 位,則可以對從最高位到第 j+1 位都與 n1 相同,而第 j 位為 0的數求和。
對於這些數,其 j10 位與 n1 的異或值就是 1+2+3+...+((1<<j)1);第 j 位與 n1 的異或值是 1<<j;最高位到第 j+1n1 的異或值為 0.

Code

#include <bits/stdc++.h>
typedef long long LL; const LL mod = 1e9+7; LL n; void work() { if (n == 2) { printf("1\n"); return;} --n; int len = 0; LL nn = n; while (nn) ++len, nn >>= 1; LL ans = (1LL << (len-2)) % mod * (((1LL << (len-1)) - 1 + mod) % mod) % mod; (ans += n) %= mod
; for (int i = 0; i < len-1; ++i) { if (n & (1LL << i)) { if (i == 0) (ans += 1) %= mod; else (ans += (((3LL << i) % mod - 1 + mod) % mod * ((1LL << (i-1)) % mod)) % mod) %= mod; } } printf("%lld\n", ans); } int main() { while (scanf("%lld", &n) != EOF) work(); return 0; }