每一个时刻每一位可以表示成其余位的异或。
可以用bitset记录每一位和其它位之间的异或方程,记为\(f_i\)
比如对于左移13步,可以令\(f_i\leftarrow f_i\text{xor}f_{i-13}\)。
如果知道\(\mod x\)的值,我们也就知道了当前这个时刻的后\(k\)位,\(k\)是最大满足\(2^k|x\)的值。
在\(n\geq 50\)的条件下,至少有\(47\)个方程,得到\(17\)个自由元。
枚举这17个判断即可。
时间复杂度为\(O(2^{17}+N)\)。
code:
const int MAXN = 1e5 + 233;
typedef bitset<65> bs;
bs f[200];
int n, a[MAXN], pos[MAXN], gen[MAXN];
vector<bs> func;
unsigned LL seed;
bs zzz;
vector<int> fr;
bool val[64];
void gauss() {
while (func.size() > 64) func.POB();
while (func.size() < 64) func.PB(zzz);
rep(i, 64) {
bool ok = 0;
for (int j = i; j < 64; ++j) {
if (func[j][i]) {
swap(func[j], func[i]);
ok = 1;
break;
}
}
if (!ok) {
fr.PB(i);
continue;
}
rep(j, 64) {
if (j != i)
if (func[j][i])
func[j] ^= func[i];
}
}
}
bool check(unsigned LL Tmp) {
seed = Tmp;
rb(i, 1, n) {
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
if (seed % i != gen[i])
return false;
}
return true;
}
int main() {
rep(i, 64) f[i].set(i);
scanf("%d", &n);
rb(i, 1, n) scanf("%d", &a[i]), pos[a[i]] = i;
rl(i, n, 1) {
gen[i] = pos[i] - 1;
int oth = pos[i];
swap(pos[i], pos[a[i]]);
swap(a[oth], a[i]);
}
rb(T, 1, min(70, n)) {
rl(i, 63, 13) f[i] ^= f[i - 13];
rb(i, 0, 63) f[i] ^= f[i + 7];
rl(i, 63, 17) f[i] ^= f[i - 17];
rep(i, 64) if ((T >> i) & 1) { break; }
else {
bs tmp = f[i];
if ((gen[T] >> i) & 1)
tmp.set(64);
func.PB(tmp);
}
}
gauss();
rep(mask, 1 << fr.size()) {
unsigned LL tmp = 0;
rep(i, fr.size()) {
if ((mask >> i) & 1) {
tmp |= (unsigned LL)(1) << fr[i];
}
val[fr[i]] = (mask >> i) & 1;
}
rep(i, 64) {
if (func[i][i]) {
bool va = func[i][64];
rep(j, 64) if (func[i][j]) va ^= val[j];
if (va) {
tmp |= (unsigned LL)(1) << i;
}
}
}
if (check(tmp)) {
cout << tmp << endl;
return 0;
}
}
return 0;
}
假设我们已知路径求分配方案。
考虑将\(k\)元,分配到\(y\)个路径:
由于\(\frac{1}{x}\)越往后减少越缓慢,所以分配的一定是越平均越好,这个是可以\(O(1)\)计算的。
其实只需要知道有多少路径经过2人和1人,设为\(x\)和\(y\)。
对于每一个\(x\),其实只需要找打对应最小的\(y\),这个部分可以直接枚举相遇点和相离点,\(O(N^2)\)预处理。
对于每一个\(x,y\),考虑各分配多少,由于这是两个凸函数相加,所以是单峰的,直接三分即可。
code:
const int MAXN = 5005;
int n, m, k, s1, t1, s2, t2;
vector<int> g[MAXN];
int dis[MAXN][MAXN];
int dp[MAXN];
double calc(int tot, int cnt) {
double have = tot / cnt;
double rst = tot % cnt;
double oth = cnt - rst;
return oth * (1 - 1 / (1 + have)) + rst * (1 - 1 / (1 + (have + 1.0)));
}
int main() {
scanf("%d%d%d", &n, &m, &k);
rb(i, 1, m) {
int u, v;
scanf("%d%d", &u, &v);
g[u].PB(v);
g[v].PB(u);
}
memset(dis, 63, sizeof(dis));
rb(i, 1, n) {
queue<int> q;
q.push(i);
dis[i][i] = 0;
while (!q.empty()) {
int now = q.front();
q.pop();
for (auto it : g[now])
if (dis[i][it] == INF) {
dis[i][it] = dis[i][now] + 1;
q.push(it);
}
}
}
scanf("%d%d%d%d", &s1, &t1, &s2, &t2);
memset(dp, 63, sizeof(dp));
rb(i, 1, n) rb(j, 1, n) if (dis[i][j] != INF && dis[s1][i] != INF && dis[s2][i] != INF &&
dis[j][t1] != INF && dis[j][t2] != INF)
check_min(dp[dis[i][j]], dis[s1][i] + dis[s2][i] + dis[j][t1] + dis[j][t2]);
dp[0] = dis[s1][t1] + dis[s2][t2];
double rest = 1e18;
rb(i, 0, n) {
if (dp[i] != INF) {
double tmp = 2.0 * i + dp[i];
if (!i && dp[i] == 0) {
rest = 0;
break;
}
if (i == 0)
tmp -= calc(k, dp[i]);
else if (dp[i] == 0)
tmp -= 2.0 * calc(k, i);
else {
int l = 0, r = k;
while (l < r) {
int mid = (l + r) >> 1;
if (2.0 * calc(mid, i) + calc(k - mid, dp[i]) <
2.0 * calc(mid + 1, i) + calc(k - (mid + 1), dp[i]))
l = mid + 1;
else
r = mid;
}
tmp -= 2.0 * calc(l, i) + calc(k - l, dp[i]);
}
check_min(rest, tmp);
}
}
printf("%.15Lf\n", rest);
return 0;
}
离线处理询问。
扫描右端点,遇到一个值\(x\)时翻转当前位置和\(x\)上一次出现的位置之间的奇偶状态,若是奇数则带来1的贡献。
对于询问,只需要查询\([l,r]\)中的历史版本和。
可以用线段树实现,具体实现方法:
对于每一个节点记录:
pushdown:
计算从上一次更新经过的时间,加到对应状态上。
根据各状态的时间算对历史答案的贡献。
将各状态的时间传递到儿子,需要更具儿子处是否翻转考虑是否取反。
传递tag。
将最新的更新时间设为当前时间。
pushup:
? ans和1,0的个数直接继承儿子的值。
code:
const int N = 1 << 19;
int nowt;
struct node {
int tag, sum[2], cnt[2], t;
LL ans;
node() { memset(sum, 0, sizeof(sum)), memset(cnt, 0, sizeof(cnt)), tag = ans = t = 0; }
};
node tree[N + N];
LL ans[N];
vector<mp> que[N];
map<int, int> pre;
void pd(int now) {
tree[now].sum[tree[now].tag] += nowt - tree[now].t;
tree[now].t = nowt;
tree[now].ans += 1ll * tree[now].cnt[1] * tree[now].sum[0] + 1ll * tree[now].cnt[0] * tree[now].sum[1];
if (now < N) {
bool lt, rt;
lt = tree[now << 1].tag;
rt = tree[now << 1 | 1].tag;
rep(i, 2) {
tree[now << 1].sum[lt ^ i] += tree[now].sum[i];
tree[now << 1 | 1].sum[rt ^ i] += tree[now].sum[i];
}
tree[now << 1].tag ^= tree[now].tag;
tree[now << 1 | 1].tag ^= tree[now].tag;
tree[now << 1].t = tree[now << 1 | 1].t = nowt;
}
if (tree[now].tag)
swap(tree[now].cnt[0], tree[now].cnt[1]);
tree[now].tag = 0;
tree[now].sum[0] = tree[now].sum[1] = 0;
}
void pu(int now) {
tree[now].cnt[0] = tree[now << 1].cnt[0] + tree[now << 1 | 1].cnt[0];
tree[now].cnt[1] = tree[now << 1].cnt[1] + tree[now << 1 | 1].cnt[1];
tree[now].ans = tree[now << 1].ans + tree[now << 1 | 1].ans;
}
void modify(int a, int b, int now = 1, int l = 1, int r = N + 1) {
pd(now);
if (r <= a || l >= b)
return;
if (r <= b && l >= a) {
tree[now].tag = 1;
pd(now);
return;
}
int mid = (l + r) >> 1;
modify(a, b, now << 1, l, mid);
modify(a, b, now << 1 | 1, mid, r);
pu(now);
}
LL query(int a, int b, int now = 1, int l = 1, int r = N + 1) {
pd(now);
if (r <= a || l >= b) {
return 0;
}
if (r <= b && l >= a) {
return tree[now].ans;
}
int mid = (l + r) >> 1;
LL tmp = query(a, b, now << 1, l, mid) + query(a, b, now << 1 | 1, mid, r);
return tmp;
}
int n, a[N], m;
int main() {
rl(i, N + N - 1, 1) if (i >= N) tree[i].cnt[0] = 1;
else tree[i].cnt[0] = tree[i << 1].cnt[0] << 1;
scanf("%d", &n);
rb(i, 1, n) scanf("%d", &a[i]);
scanf("%d", &m);
rb(i, 1, m) {
int l, r;
scanf("%d%d", &l, &r);
que[r].PB(II(l, i));
}
rb(i, 1, n) {
modify(pre[a[i]] + 1, i + 1);
nowt++;
for (auto it : que[i]) {
ans[it.SEC] = query(it.FIR, i + 1);
}
pre[a[i]] = i;
}
rb(i, 1, m) printf("%lld\n", ans[i]);
return 0;
}
原文:https://www.cnblogs.com/gary-2005/p/14710777.html