牛客算法周周练15(D dfs序+线段树维护区间平方和 E 数学推导题 )

题目链接

D-树上求和

做法:经典的题了,跑一个dfs序,然后用线段树维护即可,至于如何维护区间平方和。

若之前已经维护好了s 为某个区间的平方和,sum为区间和。

新加一个数x:

假设区间  [1,3]   有 a1  a2  a3  

s=a1*a1+a2*a2+a3*a3;

加入x

news= (a1+x)*(a1+x)+(a2+x)*(a2+x)+(a3+x)*(a3+x)

拆开:

news=a1*a1+2*a1*x+x*x+a2*a2+2*a2*x+x*x+a3*a3+2*a3*x+x*x

=>

news=a1*a1+a2*a2+a3*a3+2*a1*x+2*a2*x+2*a3*x+x*x+x*x+x*x

=>

news=s+sum*2*x+(3-1+1)*x*x

所以区间维护s  和 sum  即可

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb emplace_back
#define pii pair<int,int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const ll mod = 23333;
const int N = 1e5 + 10;
vector<int> G[N];
int n, m, dfn[N], cnt, sz[N];
ll sum[4 * N], s[4 * N], lazy[4 * N], a[N], ran[N];
void dfs(int u,int fa)
{
    dfn[u] = ++cnt;
    ran[cnt] = u;
    sz[u] = 1;
    for(int v:G[u]){
        if(v == fa) continue;
        dfs(v, u);
        sz[u] += sz[v];
    }
}

void add(ll &x, ll y)
{
    x=(x + y) % mod;
}

void pushdown(int id,int l,int r)
{
    if(lazy[id]){
        int mid = l + r>>1;

        add(lazy[id<<1], lazy[id]);
        add(lazy[id<<1|1], lazy[id]);

        add(s[id<<1], lazy[id]*lazy[id]%mod*(mid-l+1)%mod+2ll*sum[id<<1]%mod*lazy[id]%mod);
        add(s[id<<1|1], lazy[id]*lazy[id]%mod*(r-mid)%mod+2ll*sum[id<<1|1]%mod*lazy[id]%mod);

        add(sum[id<<1], lazy[id]*(mid-l+1)%mod);
        add(sum[id<<1|1], lazy[id]*(r-mid)%mod);

        lazy[id]=0;
    }
}

void pushup(int id)
{
    sum[id]=(sum[id<<1]+sum[id<<1|1])%mod;
    s[id]=(s[id<<1]+s[id<<1|1])%mod;
}

void up(int id,int l,int r,int ql,int qr,ll val)
{
    if(ql<=l&&r<=qr){
        add(lazy[id], val);
        add(s[id], val*val%mod*(r-l+1)%mod+sum[id]*val%mod*2%mod);
        add(sum[id], val*(r-l+1)%mod);
        return ;
    }
    pushdown(id, l, r);
    int mid = l + r>>1;
    if(ql <= mid) up(id<<1, l, mid, ql, qr, val);
    if(qr > mid) up(id<<1|1, mid + 1, r, ql, qr, val);
    pushup(id);
}

ll qu(int id,int l,int r,int ql,int qr)
{
    if(ql <= l && r <= qr) return s[id];
    pushdown(id, l, r);
    int mid = l + r >> 1;

    ll ans = 0;
    if(ql <= mid) add(ans, qu(id << 1, l, mid, ql, qr));
    if(qr > mid) add(ans, qu(id <<1|1, mid + 1, r, ql, qr));
    return ans;
}

void build(int id, int l, int r)
{
    if(l == r){

        sum[id] = a[ran[l]] % mod;
        s[id] = sum[id] * sum[id] % mod;
        ///printf("l:%d dfn[l]:%d %lld %lld\n", l, dfn[l], sum[id], s[id]);
        return ;
    }
    int mid = l + r >> 1;
    build(id<<1, l, mid);
    build(id<<1|1, mid+1, r);
    pushup(id);
}


int main()
{
    n = read(), m = read();
    rep(i, 1, n) a[i] = read();

    rep(i, 2, n){
        int u = read(), v = read();
        G[u].push_back(v);
        G[v].push_back(u);
    }

    dfs(1, 1);
    //rep(i,1,n) printf("i:%d dfn:%d\n",i,dfn[i]);
    build(1, 1, n);

    while(m--){
        int ty = read(), x = read();
        int l = dfn[x], r = dfn[x] + sz[x] - 1;

        if(ty == 1){
            int y = read();

            up(1, 1, n, l, r, y);
        }
        else{
            //printf("x:%d dfn:%d l:%d r:%d\n",x,dfn[x],l,r);
            printf("%lld\n", qu(1, 1, n, l, r));
        }
    }
}

E-算式子

做法:参考来自 链接

图片做法来自:博客

x/ai 地方类似于某种差分思想

#include<bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
typedef long long ll;
int n, m, x;
ll ans[N], sum[N];
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i){
        scanf("%d",&x);
        sum[x]++;
    }

    for(int i = 1; i <= m; ++i){
        if(!sum[i]) continue;
        for(int j = i; j <= m; j += i){
            ans[j] += sum[i];
        }
    }
//    for(int i = 1; i <= m; ++i) printf("%lld ",ans[i]);
//    puts("");

    for(int i = 1; i <= m; ++i) ans[i] += ans[i-1], sum[i] += sum[i-1];

    ll res = 0;
    for(int i = 1; i <= m; ++i){
        //printf("ans:%lld\n",ans[i]);
        for(int j = i; j <= m; j+=i){
            int l = j, r = min(j + i - 1, m);
            ans[i] += (sum[r] - sum[l - 1]) * (j / i);
        }

        res ^= ans[i];
    }
    printf("%lld\n", res);
}

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/107356429