加帕里的聚会 (树链剖分 双标记线段树)

加帕里的聚会

256MB / 1s ; japari.cpp / c / pas / in / out

【题目描述】
加帕里公园里有n个区域,n-1条道路将它们连接到了一起,形成了一个树的结构。开始时,第i个区域有Ai个friends,但是由于砂之星的作用,有时从x区域到y区域的简单路径上的所有区域的friends数量都会增加v,有时从x区域到y区域的简单路径上所有区域的friends数量都会变成v。
有时,从x区域到y区域的简单路径上所有区域的friends想要聚会,聚会时需要从所有参加聚会的friends中选择一部分作为staff。加帕里的friends都很喜欢质数,因此她们希望staff和非staff的参与者的数量都是质数。
请你告诉她们,每次聚会是否存在一种方案满足她们的要求。

【输入格式】
第一行两个整数n,m,表示friends数量和事件数量。
接下来一行n个数,第i个数表示Ai。
接下来n-1行,每行2个数x,y,表示x和y之间有一条无向道路。
接下来m行,每行第一个整数opt表示事件类型。
若opt=1,接下来三个整数x,y,v,表示从x区域到y区域的简单路径上的所有区域的friends数量都增加v。
若opt=2,接下来三个整数x,y,v,表示从x区域到y区域的简单路径上的所有区域的friends数量都变为v。
若opt=3,接下来两个整数x,y,表示从x区域到y区域的简单路径上的所有区域的friends进行聚会。

【输出格式】
对于每个3事件,若可以让staff和非staff的数量都为质数,输出一行SUGOI,否则输出一行TANOSHI。

【样例数据】
japari1.in
3 4
1 2 1
2 1
3 2
2 2 1 4
1 3 3 -1
2 2 2 2
3 1 3
japari1.out
SUGOI
japari2.in
4 6
2 4 0 0
2 1
3 2
4 3
2 1 4 2
1 4 4 9
1 3 2 -2
2 4 2 5
3 1 4
3 4 4
japari2.out
TANOSHI
SUGOI

【样例解释】
在第一个样例中,三个区域的friends数量依次为4、2、0,询问的friends和为6,可以分成两组,每组的friends数量都为3。
在第二个样例中,四个区域的friends数量依次为2、5、5、5,第一个询问的friends和为17,无法分成两组。第二个询问的friends和为5,可以分为两组,每组的friends数量分别为2、3。

【数据范围】
对于30%的数据,n,m≤5000。
对于另30%的数据,对于i>1,第i个区域与第i-1个区域直接相连。
对于100%的数据,1≤n,m≤100000,1≤x,y≤n,一直满足0≤Ai,S≤10000000。在增加事件中v可能为负数。

思路:
链修改,链赋值,链求和并判断是否可以将这个和拆分为两个质数的和。直接链剖或LCT即可维护,根据哥德巴赫猜想若这个数为偶数则一定可以拆分,否则其中一个必定为2,线性筛判定质数即可。
好久没写那么长的代码了,查错能力简直low到爆。
(增值标记和赋值标记千万想清楚呀)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int lim=1e7+5,N=1e5+5,INF=0x3f3f3f3f;

bool isnot[lim];
int n,m,primed[lim/10],tot=0;
int top[N],dep[N],fa[N],son[N],siz[N],head[N],in[N],out[N],idc=0;
int seq[N],re[N],a[N],idx=0;
int sum[N<<2],sign[N<<2],flag[N<<2];

struct Edge {
    int to, nxt;
}ed[N<<1];

inline void adde(int u,int v) {
    ed[++idc].to = v;
    ed[idc].nxt = head[u];
    head[u] = idc;
}

inline void init() {
    for(register int i=2; i<lim; ++i) {
        if(!isnot[i]) primed[++tot]=i;
        for(int j=1; j<=tot && i*primed[j]<lim; ++j) {
            isnot[i*primed[j]] = true;
            if(i % primed[j] == 0) break;
        }
    }
}

inline void dfs1(int u) {
    siz[u] = 1; 
    for(int k=head[u]; k; k=ed[k].nxt) {
        int v = ed[k].to;
        if(v == fa[u]) continue;
        fa[v] = u;
        dep[v] = dep[u] + 1;
        dfs1( v );
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]]) son[u] = v; 
    }
}

inline void dfs2(int u, int tp) {
    seq[++idx] = u; 
    in[u] = idx;
    top[u] = tp;
    if( son[u] ) dfs2( son[u], tp );
    for(int k=head[u]; k; k=ed[k].nxt) {
        int v = ed[k].to;
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
    out[u] = idx;
}

inline void pushdown(int pos,int l, int r) {
    int len = r - l + 1;
    int mid = (l + r) >> 1;  
    if (sign[pos]!=INF) {
        sign[pos<<1] = sign[pos];  
        sign[pos<<1|1] = sign[pos];  
        sum[pos<<1] = (mid-l+1) * sign[pos];  
        sum[pos<<1|1] = (r-mid) * sign[pos];    
        flag[pos<<1]=flag[pos<<1|1]=0;
        sign[pos]=INF;
    }
    if (flag[pos]!=0) {
        flag[pos<<1]+=flag[pos];
        flag[pos<<1|1]+=flag[pos];
        sum[pos<<1] += (mid-l+1) * flag[pos];
        sum[pos<<1|1] += (r-mid) * flag[pos];
        flag[pos]=0;
    }
}

inline void build(int l, int r, int pos){
    flag[pos] = 0; sign[pos] = INF;//
    if(l == r){
        sum[pos] = re[l];
        return ;
    }
    int mid = (l + r) >> 1;
    build(l, mid, pos<<1);
    build(mid+1, r, pos<<1|1);
    sum[pos] = sum[pos<<1] + sum[pos<<1|1];
}

inline void qu_add(int pos, int l, int r, int ll, int rr, int val){
    if(ll==l && r==rr){
        flag[pos] += val;
        sum[pos] += (r-l+1)*val;
        return;
    }
    pushdown(pos, l, r);
    int mid = (l+r)>>1;
    if(rr<=mid) qu_add(pos<<1, l, mid, ll, rr, val);
    else if(ll>mid) qu_add(pos<<1|1, mid+1, r, ll, rr, val);
    else{
        qu_add(pos<<1, l, mid, ll, mid, val);
        qu_add(pos<<1|1, mid+1, r, mid+1, rr, val); 
    } 
    sum[pos] = sum[pos<<1] + sum[pos<<1|1];  
}  

inline void qu_modify(int pos, int l, int r, int ll, int rr, int val){
    if(ll==l && r==rr){
        sign[pos] = val;  /**/flag[pos] = 0;
        sum[pos] = (r-l+1)*val;  
        return;
    }  
    pushdown(pos, l, r);  
    int mid = (l+r)>>1;  
    if(rr<=mid) qu_modify(pos<<1, l, mid, ll, rr, val);
    else if(ll>mid) qu_modify(pos<<1|1, mid+1, r, ll, rr, val);
    else{
        qu_modify(pos<<1, l, mid, ll, mid, val);
        qu_modify(pos<<1|1, mid+1, r, mid+1, rr, val); 
    } 
    sum[pos] = sum[pos<<1] + sum[pos<<1|1];  
}  

inline int qu_query(int pos, int l, int r, int ll, int rr){  
    if(ll==l && r==rr){
        return sum[pos];  
    }  
    pushdown(pos, l, r);  
    int mid = (l+r)>>1;  
    if(rr<=mid) return qu_query(pos<<1, l, mid, ll, rr);  
    else if(ll>mid) return qu_query(pos<<1|1, mid+1, r, ll, rr);  
    else return qu_query(pos<<1, l, mid, ll, mid) + qu_query(pos<<1|1, mid+1, r, mid+1, rr);  
} 

inline void modify(int u, int v, int val, int type) {
    while (top[u] != top[v]) {
        if( dep[top[u]] < dep[top[v]] ) swap(u,v);
        if(type == 1) qu_add(1, 1, idx, in[top[u]], in[u], val);
        else qu_modify(1, 1, idx, in[top[u]], in[u], val);
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u,v);
    if(type == 1) qu_add(1, 1, idx, in[v], in[u], val);
    else qu_modify(1, 1, idx, in[v], in[u], val);
}

inline int query(int u, int v) {
    int rt = 0;
    while( top[u] != top[v] ) {
        if( dep[top[u]] < dep[top[v]] ) swap(u,v);
        rt += qu_query(1, 1, idx, in[top[u]], in[u]);
        u = fa[top[u]];
    }
    if( dep[u] < dep[v] ) swap(u,v);
    rt += qu_query(1, 1, idx, in[v], in[u]);
    return rt;
}

inline int read() {
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}

int main() {
    freopen("japari.in","r",stdin);
    freopen("japari.out","w",stdout);
    init();
    n=read(), m=read();
    for(register int i=1; i<=n; ++i) a[i] = read();
    for(register int i=1; i<n; ++i) {
        int u=read(), v=read();
        adde(u,v); adde(v,u);
    }
    dfs1(1);
    dfs2(1, 1);
    for(int i=1; i<=n; i++){
        re[in[i]] = a[i];
    }
    build(1, idx, 1);
    while( m-- ){
        int opt; scanf("%d", &opt);  
        if(opt == 1){
            int x=read(), y=read(), v=read();
            modify(x,y,v,1);
        }
        if(opt == 2){  
            int x=read(), y=read(), v=read();
            modify(x,y,v,2);
        }  
        if(opt == 3){  
            int x=read(), y=read();
            int cc = query(x, y);
            //judge( cc );
            if (cc<4) puts("TANOSHI");
            else if (cc&1) puts(isnot[cc-2]?"TANOSHI":"SUGOI");
            else puts("SUGOI");
            //printf("Y %d\n", cc);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值