https://www.luogu.org/contest/show?tid=3420
给出a,b,c三个数的与(&)x或(|)y和(sum)z,求a,b,c. 希望a尽可能小,a相同则b尽可能小,b相同则c尽可能小.
a = x, b = z - x - y, c = y
将x,y,z用二进制表示,先考虑x,y. 若x的第i位上为1,则a,b,c的第i位上均为1,若y的第i位为0,则a,b,c的第i位上为0. 否则a,b,c三个数,第i位上上总有1个或2个数为1. 为使a最小,则将这1个或2个1全部放在b,c上,a = x. 总有一个1在c上,c = y. 于是b = z - x - y.
#include
using namespace std;
long long a,b,c;
int main() {
while(scanf("%lld%lld%lld",&a,&b,&c)!=EOF)
printf("%lld %lld %lld\n",a,c-a-b,b);
return 0;
}
对长度为n的序列建立线段树维护区间和。进行若干次区间加,每次暴力修改到底后,从根节点开始每次等概率地选择一个子节点进入,直到进入叶子节点。将一路经过的叶子节点权值和累加,求权值和的期望值。
期望值 = ∑val[i]∗(1/(2d[i]) ,val[i]表示点线段树中的点i保存的区间和,d[i]为点i在线段树中的深度.
考虑每次对单点i加add,实质为 ans+=add∗∑1/(2d[j]) ,j为点i在线段树中的节点到根节点路径上的所有点。于是对于点1~n,处理出 a[i]=∑1/(2d[j]) . 而每次修改的是区间,那么对a[i]维护一个前缀和数组sum[i]即可修改。
在树上问题中涉及每次都从当前点到根节点的操作时,可以考虑前缀和思想解题。
亲测long double 保存a[] TLE 3个点,于是在保存时乘seed = 2^(ceil(log(n)/log(2)),开long long.最后/seed即可,避免爆long long,先约去seed与qwq的公约数。
#include
#include
using namespace std;
typedef long long LL;
const int sm = 2e6 + 5;
int n,m,u;
LL g,sd,qwq,ans;
LL a[sm],sum[sm],val[sm];
int Max(int x,int y) { return x>y?x:y; }
template <typename T> void read(T &x) {
char ch = getchar(); x = 0; int f = 1;
while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') x = x*10 + ch - '0', ch=getchar();
x *= f;
}
void Build(int rt,int l,int r,int d) {
val[rt] = (1<<(sd-d)) + val[rt>>1];
if(l==r) {
read(u);
a[l] = val[rt];
ans += 1ll * a[l] * u;
return;
}
int m = (l + r) >> 1;
Build(rt<<1,l,m,d+1);
Build(rt<<1|1,m+1,r,d+1);
}
LL gcd(LL x,LL y) { return y==0?x:gcd(y,x%y); }
int main() {
int u,v,w;
read(n),read(m),read(qwq);
sd = ceil(log2(n));
Build(1,1,n,0);
sd = 1ll << sd;
g = gcd(sd,qwq);
sd/=g,qwq/=g;
for(int i = 1; i <= n; ++i)
sum[i] = sum[i-1] + a[i];
for(int i = 1; i <=m; ++i) {
read(u),read(v),read(w);
ans += 1ll * (sum[v] - sum[u-1]) * w;
`
printf("%lld\n",ans*qwq/sd);
}
return 0;
}
给出一颗以1为根n个节点的树并定义 sum ,对每个节点k按照如下方法计算 subsum ,将k以及它的子树节点按照权值从大到小v排序后得到一个权值序列 val1,val2......vali , subsum[k]=val1∗i+val2∗(i−1)+...vali∗1 ,求 sum=∑ni=1subsum[i]
考虑每个点的权值对于最终答案的贡献次数。
节点i的权值被贡献,一种可能是计算 subsum[i] ,另一种可能是计算i的祖先节点g的 subsum[g] 时,在g的子树中存在点 j,valj<vali . 因此将点按照val从小到大排序,依次插入。插入点i即将点i到点1的路径上所有点的计数器+1,统计点i到点1的路径上所有点的计数器的和 S ,S即为点i的权值对于最终答案的贡献次数。
树链剖分+线段树实现。
#include
#include
#include
#define f first
#define s second
using namespace std;
typedef long long LL;
const int sm = 5e5 + 10;
const int Mod = 1e9+7;
LL ret;
int n,tot;
int to[sm<<1],nxt[sm<<1],hd[sm];
LL cnt[sm<<2],mk[sm<<2];
int dp[sm],fa[sm],sn[sm],sz[sm],top[sm],Bn[sm];
pair<int,int> p[sm];
void Swap(int &x,int &y) { int t = x; x = y; y = t; }
void read(int &x) {
char ch = getchar(); x = 0;
while(ch > '9' || ch < '0') ch = getchar();
while(ch>='0'&&ch<='9') x = x*10 + ch - '0', ch = getchar();
}
void Add(int u,int v) {
to[++tot] = v, nxt[tot] = hd[u], hd[u] = tot;
to[++tot] = u, nxt[tot] = hd[v], hd[v] = tot;
}
void Dfsa(int x,int ff) {
fa[x] = ff, dp[x] = dp[ff]+1, sz[x] = 1;
for(int i = hd[x];i;i = nxt[i])
if(to[i]!=ff) {
Dfsa(to[i],x);
sz[x]+=sz[to[i]];
if(sz[to[i]] > sz[sn[x]])
sn[x] = to[i];
}
}
void Dfsb(int x,int Top) {
Bn[x] = ++tot, top[x] = Top;
if(sn[x]) Dfsb(sn[x],Top);
for(int i = hd[x]; i; i = nxt[i])
if(to[i]!=fa[x]&&to[i]!=sn[x])
Dfsb(to[i],to[i]);
}
void Pd(int rt,int l,int m,int r) {
cnt[rt<<1] = (cnt[rt<<1] + (m-l+1)*mk[rt]) % Mod;
cnt[rt<<1|1] = (cnt[rt<<1|1] + (r-m)*mk[rt]) % Mod;
mk[rt<<1] = (mk[rt<<1] + mk[rt]) % Mod;
mk[rt<<1|1] = (mk[rt<<1|1] + mk[rt]) % Mod;
mk[rt] = 0;
}
void Ins(int rt,int l,int r,int a,int b) {
if(a<=l&&r<=b) {
mk[rt] = (mk[rt] + 1) % Mod;
cnt[rt] = (cnt[rt] + (r-l+1)) % Mod;
return;
}
int m = (l + r) >> 1;
if(mk[rt]) Pd(rt,l,m,r);
if(a <= m) Ins(rt<<1,l,m,a,b);
if(b > m) Ins(rt<<1|1,m+1,r,a,b);
cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1];
}
LL Query(int rt,int l,int r,int a,int b) {
if(a<=l&&r<=b) return cnt[rt];
int m = (l + r) >> 1; LL ans = 0;
if(mk[rt]) Pd(rt,l,m,r);
if(a <= m) ans = (ans + Query(rt<<1,l,m,a,b)) % Mod;
if(b > m) ans = (ans + Query(rt<<1|1,m+1,r,a,b)) % Mod;
cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1];
return ans;
}
LL Calc(int x,int y) {
LL sum = 0;
while(top[x]!=top[y]) {
if(dp[x] < dp[y]) Swap(x,y);
Ins(1,1,n,Bn[top[x]],Bn[x]);
sum = (sum + Query(1,1,n,Bn[top[x]],Bn[x])) % Mod;
x = fa[top[x]];
}
if(dp[x] < dp[y]) Swap(x,y);
Ins(1,1,n,Bn[y],Bn[x]);
sum = (sum + Query(1,1,n,Bn[y],Bn[x])) % Mod;
return sum;
}
int main() {
int u,v;
read(n);
for(int i = 1; i < n; ++i)
read(u),read(v),Add(u,v);
for(int i = 1; i <= n; ++i)
read(p[i].f),p[i].s = i;
sort(p+1,p+n+1);
Dfsa(1,0), tot = 0, Dfsb(1,1);
for(int i = 1; i <= n; ++i)
ret = (ret + 1ll*Calc(1,p[i].s)*p[i].f) % Mod;
printf("%lld\n",ret);
return 0;
}