1、【F. Strange Memory】
题目来源:
2020 CCPC 长春站 F. Strange Memory
难度:
铜牌🥉+(参照比赛正式队情况)
题意:
给定一棵有根树,根为 1 1 1,对于节点 i i i 有一个权值 a i a_i ai,求 ∑ i = 1 n ∑ j = i + 1 n [ a i ⊕ a j = a l c a ( i , j ) ] ( i ⊕ j ) \sum_{i=1}^n\sum_{j=i+1}^n[a_i\oplus a_j=a_{lca(i,j)}](i\oplus j) ∑i=1n∑j=i+1n[ai⊕aj=alca(i,j)](i⊕j),其中 l c a ( i , j ) lca(i,j) lca(i,j) 表示节点 i i i 与 j j j 的最近公共祖先。
算法:
树上启发式合并(dsu on tree)
思路:
遍历以节点
k
k
k 作为点
i
i
i 的祖先时,暴力枚举与点
i
i
i 不在同一棵
k
k
k 的儿子子树的点
j
j
j(确保了
l
c
a
(
i
,
j
)
=
k
lca(i,j)=k
lca(i,j)=k),满足
a
i
⊕
a
j
=
a
k
a_i\oplus a_j=a_k
ai⊕aj=ak。
每遍历一棵点
k
k
k 的儿子子树后,将所遍历的点的映射关系
(
a
i
→
i
)
(a_i\rightarrow i)
(ai→i) 用
v
e
c
t
o
r
vector
vector 记录,再在遍历下一棵儿子子树时,假设当前位于点
j
j
j,只需将答案加上
v
e
c
t
o
r
[
a
k
⊕
a
j
]
vector[a_k\oplus a_j]
vector[ak⊕aj] 中的每一个值异或上
j
j
j 。
考虑dsu on tree优化复杂度,对于重儿子的映射关系,父亲可以直接继承。
#include<bits/stdc++.h>
#define ll long long
#define L(i,j,k) for(ll i=(j);i<=(k);i++)
#define R(i,j,k) for(ll i=(j);i>=(k);i--)
#define inf 0x3f3f3f3f3f3f3f3f
#define vec vector
#define pll pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define MS(i,j) memset(i,j,sizeof (i))
const ll N=1e6+10,M=10;
const ll mod=998244353,mmod=mod-1;
const double pi=acos(-1),eps=1e-8;
using namespace std;
ll fmul(ll a,ll b){a%=mod;b%=mod;ll res=0;while(b){if(b&1){res+=a;res%=mod;}a<<=1;if(a>=mod)a%=mod;b>>=1;}return res;}
ll gcd(ll x,ll y){if(y==0) return x;return gcd(y,x%y);}
ll fksm(ll a,ll b){ll r=1;if(b<0)b+=mod-1;for(a%=mod;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}//a 分母; b MOD-2
ll lowbit(ll x){return x&(-x);}
ll dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};
ll m,n,t,x,y,z,l,r,u,v,k,p,pp,nx,ny,nz,num[N],sum[N],mn,mx,ans;
ll lim,pos,tot,cnt,key,block;
ll a[N],b[N];
double dans;
bool vis[N],flag;
char s[N],mapp,zz[5];
struct qq{ll x,y,z;}q;
bool cmp(qq u,qq v){
return u.x>v.x;
}
bool cmp1(qq u,qq v){
return u.x<v.x;
}
bool cmpl(ll u,ll v){return u>v;}
struct cmps{bool operator()(ll u,ll v){
return u>v;
}};//shun序
vec<ll>sv[N];
struct tree{ll fa,dep,dfn,siz,son,w;}tr[N];
struct edge{
ll cnt,hed[N],to[N*2],nxt[N*2];
void add(ll u,ll v){to[++cnt]=v;nxt[cnt]=hed[u];hed[u]=cnt;}
void ADD(ll u,ll v){add(u,v);add(v,u);}
void clear(ll n){cnt=0;L(i,1,n)hed[i]=0;}
}eg;
void dfs0(ll u,ll ac){//计算重儿子
tr[u].fa=ac;
tr[u].dep=tr[tr[u].fa].dep+1;
tr[u].siz=1;
for(ll i=eg.hed[u];i;i=eg.nxt[i]){
ll v=eg.to[i];
if(v!=ac){
dfs0(v,u);
tr[u].siz+=tr[v].siz;
if(!tr[u].son||tr[v].siz>tr[tr[u].son].siz)tr[u].son=v;
}
}
}
void dfs2(ll u,ll r){//计算一棵子树对答案的贡献
ll p=a[u]^a[r];
if(!sv[p].empty()){
for(auto x:sv[p]){
ans+=x^u;
}
}
for(ll i=eg.hed[u];i;i=eg.nxt[i]){
ll v=eg.to[i];
if(v!=tr[u].fa)dfs2(v,r);
}
}
void dfs3(ll u){//记录这棵子树的映射关系
if(sv[a[u]].empty())b[++cnt]=a[u];
sv[a[u]].pb(u);
for(ll i=eg.hed[u];i;i=eg.nxt[i]){
ll v=eg.to[i];
if(v!=tr[u].fa)dfs3(v);
}
}
void dfs1(ll u,bool type){//遍历节点u作为lca
for(ll i=eg.hed[u];i;i=eg.nxt[i]){
ll v=eg.to[i];
if(v!=tr[u].fa&&v!=tr[u].son)dfs1(v,1);
}
if(tr[u].son)dfs1(tr[u].son,0);
if(sv[a[u]].empty())b[++cnt]=a[u];
sv[a[u]].pb(u);
for(ll i=eg.hed[u];i;i=eg.nxt[i]){
ll v=eg.to[i];
if(v!=tr[u].fa&&v!=tr[u].son){
dfs2(v,u);
dfs3(v);
}
}
if(type){L(i,1,cnt)sv[b[i]].clear();cnt=0;}//type表示是否为轻儿子
}
void solve(){
scanf("%lld",&n);
L(i,1,n)scanf("%lld",&a[i]);
L(i,1,n-1){
scanf("%lld%lld",&x,&y);
eg.ADD(x,y);
}
ans=0;
dfs0(1,0);
dfs1(1,1);
printf("%lld\n",ans);
}
int main(){
// ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
// cout<<fixed<<setprecision(12);//精度
ll Case=1;
//scanf("%lld",&Case);
while(Case--)solve();
}
2、【K. Ragdoll】
题目来源:
难度:
银牌🥈+(参照比赛正式队情况)
题意:
给定一个 n n n 个节点 0 0 0 条边的无向图(最初每个节点互不连通),对于节点 i i i 有一个权值 a i a_i ai,接下来有 m m m 次操作,每次操作有以下三种:
- 新增一个节点 x x x,权值为 v v v;
- 连接节点 x x x 与节点 y y y ;
- 将节点 x x x的权值改为 v v v。
每次操作完后,输出有多少个节点对 ( i , j ) (i,j) (i,j),满足节点 i i i 与节点 j j j 在同一连通块中,且 g c d ( a i , a j ) = a i ⊕ a j gcd(a_i,a_j)=a_i\oplus a_j gcd(ai,aj)=ai⊕aj。(节点对 ( i , j ) (i,j) (i,j) 与 ( j , i ) (j,i) (j,i) 被视作同一个节点对)
算法:
并查集+启发式合并
思路:
预处理所有的
g
c
d
(
x
,
y
)
=
x
⊕
y
gcd(x,y)=x\oplus y
gcd(x,y)=x⊕y。
枚举
d
=
g
c
d
(
x
,
y
)
d=gcd(x,y)
d=gcd(x,y),枚举
i
i
i使得
x
=
i
d
x=id
x=id,那么
y
=
x
⊕
d
y=x\oplus d
y=x⊕d,如果
g
c
d
(
x
,
y
)
=
d
gcd(x,y)=d
gcd(x,y)=d 就记录
(
x
,
y
)
(x,y)
(x,y) 对。
预处理复杂度
w
l
o
g
w
wlogw
wlogw。
考虑更新答案的操作只有合并或者改值,我们考虑对每一个连通块用一个
s
e
t
set
set 维护权值。
对于合并操作,遍历小连通块的
s
e
t
set
set,依据预处理的值,去寻找大连通块的
s
e
t
set
set 中的值即可。
改值操作同理,考虑删除该值对答案的影响,答案减去该连通块中与旧值形成的对的个数;考虑新增值对答案的影响,答案加上该连通块中与新值形成的对的个数。
#include<bits/stdc++.h>
#define ll long long
#define L(i,j,k) for(ll i=(j);i<=(k);++i)
#define R(i,j,k) for(ll i=(j);i>=(k);--i)
#define inf 9e18
#define vec vector
#define pll pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define MS(i,j) memset(i,j,sizeof (i))
const ll N=1e6+10,M=10;
const ll mod=998244353,mmod=mod-1;
const double pi=acos(-1),eps=1e-8;
using namespace std;
ll fmul(ll a,ll b){a%=mod;b%=mod;ll res=0;while(b){if(b&1){res+=a;res%=mod;}a<<=1;if(a>=mod)a%=mod;b>>=1;}return res;}
ll gcd(ll x,ll y){if(y==0) return x;return gcd(y,x%y);}
ll fksm(ll a,ll b){ll r=1;if(b<0)b+=mod-1;for(a%=mod;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}
ll lowbit(ll x){return x&(-x);}
ll dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};
ll m,n,t,x,y,z,l,r,u,v,k,p,pp,nx,ny,nz,ansx,ansy,mn,mx;
ll op,lim,pos,key,block;
ll cnt,tot,num,sum,ans;
ll a[N],b[N];
double dans;
bool vis[N],flag;
char s[N],mapp,zz[5];
struct qq{ll x,y,z;}q;
bool cmp(qq u,qq v){
return u.x>v.x;
}
bool cmp1(qq u,qq v){
return u.x<v.x;
}
bool cmpl(ll u,ll v){return u>v;}
struct cmps{bool operator()(ll u,ll v){
return u>v;
}};//shun序
pair<ll,ll>pr;
vector<ll>sv[N],vans;//v.assign(m,vector<ll>(n));
//priority_queue<ll,vector<ll>,cmps>sp;
queue<ll>sq;
stack<ll>st;
map<ll,bool>mp;
set<ll>::iterator it;
bitset<M>bi;
#define sit set<node>::iterator
struct node{
ll val;
mutable ll num;
node(ll l,ll r):val(l),num(r){}
bool operator <(const node& u)const{
return val<u.val;
}
};
set<node>se[N];
void init(ll n){
L(d,1,n){
L(i,1,n){
if(i*d>n)break;
ll id=i*d,jd=(id)^d;
if(jd%d==0&&jd>0&&jd<=n){
ll j=jd/d;
if(gcd(i,j)==1){
cnt++;b[id]++;
sv[id].pb(jd);
}
}
}
}
}
ll cut(ll i,ll x){
sit it=se[i].lower_bound(node(x,0));
if(it==se[i].end())return 0;
if(it->val!=x)return 0;
return it->num;
}
void del(ll i,ll x){
sit it=se[i].lower_bound(node(x,0));
node tmp=*it;
if(tmp.num==1)se[i].erase(it);
else it->num-=1;
}
void ins(ll i,ll x,ll cs){
sit it=se[i].lower_bound(node(x,0));
if(it!=se[i].end()){
if(it->val==x)it->num+=cs;
else se[i].insert(node(x,cs));
}
else se[i].insert(node(x,cs));
}
ll pre[N],siz[N];
ll fd(ll x){
if(x==pre[x])return x;
else return pre[x]=fd(pre[x]);
}
void meg(ll x,ll y){
ll fx=fd(x),fy=fd(y);
if(fx!=fy){
if(siz[fx]>=siz[fy]){
for(node it:se[fy]){
ll is=it.num;
for(auto ip:sv[it.val]){
ll js=cut(fx,ip);
ans+=is*js;
}
}
for(node it:se[fy])ins(fx,it.val,it.num);
pre[fy]=fx;
siz[fx]+=siz[fy];
}
else{
for(node it:se[fx]){
ll is=it.num;
for(auto ip:sv[it.val]){
ll js=cut(fy,ip);
ans+=is*js;
}
}
for(node it:se[fx])ins(fy,it.val,it.num);
pre[fx]=fy;
siz[fy]+=siz[fx];
}
}
}
void solve(){
scanf("%lld%lld",&n,&m);
ans=0;
L(i,1,n+m)pre[i]=i,siz[i]=1;
L(i,1,n){
scanf("%lld",&a[i]);
se[i].insert(node(a[i],1));
}
while(m--){
scanf("%lld%lld%lld",&op,&x,&y);
if(op==3){
ll fx=fd(x);//printf("?%lld\n",fx);
for(auto ip:sv[a[x]]){
ll js=cut(fx,ip);
ans-=js;
}
del(fx,a[x]);
a[x]=y;
for(auto ip:sv[a[x]]){
ll js=cut(fx,ip);
ans+=js;
}
ins(fx,a[x],1);
}
else if(op==2){
meg(x,y);
}
else{
a[x]=y;
se[x].insert(node(y,1));
}
printf("%lld\n",ans);
}
}
int main(){
// ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
// cout<<fixed<<setprecision(12);//精度
ll Case=1;
init(200000);
//scanf("%lld",&Case);
while(Case--)solve();
}
3、【J. Joy of Handcraft】
题目来源:
2020 CCPC 绵阳站 J. Joy of Handcraft
难度:
铜牌🥉-(参照比赛正式队情况)
题意:
给定 n n n 个灯泡,每个灯泡打开关闭状态有一个变化周期,即对于第 i i i 个灯泡,在 [ 2 k t i + 1 , 2 k t i + t i ] [2kt_i+1,2kt_i+t_i] [2kti+1,2kti+ti] 时间内为打开状态,在 [ 2 k t i + t i + 1 , 2 k t i + 2 t i ] [2kt_i+t_i+1,2kt_i+2t_i] [2kti+ti+1,2kti+2ti] 时间内为关闭状态,同时,其有一个权值 x i x_i xi。
输出第 j j j 秒时( j ∈ [ 1 , m ] j∈[1,m] j∈[1,m]),所有打开状态的灯泡中的最大权值。
算法:
权值线段树
思路:
枚举每个周期不同的灯泡的最大值,对于周期为
t
t
t 的灯泡,我们只需处理
⌈
m
t
⌉
\lceil \frac{m}{t}\rceil
⌈tm⌉ 个区间,总量级为
m
l
o
g
m
mlogm
mlogm。
建立权值线段树,维护区间最大值,每次区间标记覆盖即可。
#include<bits/stdc++.h>
#define ll long long
#define L(i,j,k) for(ll i=(j);i<=(k);++i)
#define R(i,j,k) for(ll i=(j);i>=(k);--i)
#define inf 9e18
#define vec vector
#define pll pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define MS(i,j) memset(i,j,sizeof (i))
const ll N=1e6+10,M=10;
const ll mod=998244353,mmod=mod-1;
const double pi=acos(-1),eps=1e-8;
using namespace std;
ll fmul(ll a,ll b){a%=mod;b%=mod;ll res=0;while(b){if(b&1){res+=a;res%=mod;}a<<=1;if(a>=mod)a%=mod;b>>=1;}return res;}
ll gcd(ll x,ll y){if(y==0) return x;return gcd(y,x%y);}
ll fksm(ll a,ll b){ll r=1;if(b<0)b+=mod-1;for(a%=mod;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}
ll lowbit(ll x){return x&(-x);}
ll dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};
ll m,n,t,x,y,z,l,r,u,v,k,p,pp,nx,ny,nz,ansx,ansy,mn,mx;
ll op,lim,pos,key,block;
ll cnt,tot,num,sum,ans;
ll a[N],b[N];
double dans;
bool vis[N],flag;
char s[N],mapp,zz[5];
struct qq{ll x,y;}q[N];
bool cmp(qq u,qq v){
return u.y<v.y;
}
bool cmp1(qq u,qq v){
return u.x<v.x;
}
bool cmpl(ll u,ll v){return u>v;}
struct cmps{bool operator()(ll u,ll v){
return u>v;
}};//shun序
pair<ll,ll>pr;
vector<ll>sv[N],vans;//v.assign(m,vector<ll>(n));
//priority_queue<ll,vector<ll>,cmps>sp;
queue<ll>sq;
stack<ll>st;
map<ll,ll>mp;
bitset<M>bi;
struct segment{ll l,r,mx;}trs[N*4];
void push_down(ll k){
if(trs[k].mx){
ll l=k*2,r=k*2+1;
trs[l].mx=max(trs[l].mx,trs[k].mx);
trs[r].mx=max(trs[r].mx,trs[k].mx);
trs[k].mx=0;
}
}
void bd_tree(ll k,ll l,ll r){
trs[k].l=l,trs[k].r=r;
trs[k].mx=0;
if(l==r){
trs[k].mx=0;
return;
}
ll mid=(l+r)/2;
bd_tree(k*2,l,mid);
bd_tree(k*2+1,mid+1,r);
}
ll query(ll k,ll p){
if(trs[k].l==trs[k].r){
return trs[k].mx;
}
push_down(k);
ll mid=(trs[k].l+trs[k].r)/2;
if(mid>=p)return query(k*2,p);
else return query(k*2+1,p);
}
void modify(ll k,ll pl,ll pr,ll val){
if(trs[k].l>=pl&&trs[k].r<=pr){
trs[k].mx=max(trs[k].mx,val);
return;
}
push_down(k);
ll mid=(trs[k].l+trs[k].r)/2;
if(mid>=pl)modify(k*2,pl,pr,val);
if(mid+1<=pr)modify(k*2+1,pl,pr,val);
}
void solve(){
scanf("%lld%lld",&n,&m);
cnt=0;
bd_tree(1,1,m);
L(i,1,m+1)a[i]=0;
L(i,1,n){
scanf("%lld%lld",&x,&y);
x=min(x,m);
a[x]=max(a[x],y);
}
L(i,1,m){
if(a[i])q[++cnt]={i,a[i]};
}
sort(q+1,q+cnt+1,cmp);
L(i,1,cnt){
for(ll j=1;j<=m;j+=q[i].x*2){
ll k=min(m,j+q[i].x-1);
modify(1,j,k,q[i].y);
}
}
L(i,1,m){
printf("%lld",query(1,i));
if(i!=m)printf(" ");
else printf("\n");
}
}
int main(){
// ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
// cout<<fixed<<setprecision(12);//精度
ll Case=1;
scanf("%lld",&Case);
L(i,1,Case){
printf("Case #%lld: ",i);
solve();
}
}
4、【C. Rencontre】
题目来源:
难度:
银牌🥈-(参照比赛正式队情况)
题意:
给定由 n n n 个节点 n − 1 n-1 n−1 条边形成的一棵树,给定每条边的边权。有三个人,给定每个人的起点清单,每个人会从自己的清单中等概率的选择一个节点作为起点,求使三人汇集在一点的期望最小花费。
算法:
树dp
思路:
假设三个人的起点分别位于
x
,
y
,
z
x,y,z
x,y,z,那么使三人汇聚一点的最小花费为
w
(
x
,
y
)
+
w
(
x
,
z
)
+
w
(
y
,
z
)
2
\frac{w(x,y)+w(x,z)+w(y,z)}{2}
2w(x,y)+w(x,z)+w(y,z),其中
w
(
x
,
y
)
w(x,y)
w(x,y) 表示点
x
x
x 到点
y
y
y 的简单路径的权值和。因此,我们可以将问题拆解为三个子问题:求两人汇聚一点的期望最小花费。
假设
b
x
b_x
bx 表示点
1
1
1 到点
x
x
x 的简单路径权值和,则
w
(
x
,
y
)
=
b
x
+
b
y
−
2
b
l
c
a
(
x
,
y
)
w(x,y)=b_x+b_y-2b_{lca(x,y)}
w(x,y)=bx+by−2blca(x,y),其中
l
c
a
(
x
,
y
)
lca(x,y)
lca(x,y) 表示点
x
x
x 和点
y
y
y 的最近公共祖先。
对于两个人的起点清单,期望值
=
=
= 所有方案权值和
/
/
/ 方案数。所有方案权值和可以通过枚举点
x
x
x 是多少对
(
i
,
j
)
(i,j)
(i,j) 的
l
c
a
lca
lca 快速求得。
具体的说,假设分别有
m
0
,
m
1
m_0,m_1
m0,m1 个起点方案,每个起点分别是
a
0
,
i
a_{0,i}
a0,i(
i
∈
[
1
,
m
0
]
i∈[1,m_0]
i∈[1,m0])和
a
1
,
i
a_{1,i}
a1,i(
i
∈
[
1
,
m
1
]
i∈[1,m_1]
i∈[1,m1]),则方案数
=
m
0
∗
m
1
=m_0*m_1
=m0∗m1,所有方案权值和
=
m
1
∑
i
=
1
m
0
b
a
0
,
i
+
m
0
∑
i
=
1
m
1
b
a
1
,
i
−
∑
i
=
1
m
0
∑
j
=
1
m
1
b
l
c
a
(
a
0
,
i
,
a
1
,
j
)
①
=m_1\sum_{i=1}^{m_0}b_{a_{0,i}}+m_0\sum_{i=1}^{m_1}b_{a_{1,i}}-\sum_{i=1}^{m_0}\sum_{j=1}^{m_1}b_{lca(a_{0,i},a_{1,j})}①
=m1∑i=1m0ba0,i+m0∑i=1m1ba1,i−∑i=1m0∑j=1m1blca(a0,i,a1,j)①。
对于式①,枚举点
x
x
x ,如果
l
c
a
(
i
,
j
)
=
x
lca(i,j)=x
lca(i,j)=x,那么点
i
i
i 和点
j
j
j 一定在点
x
x
x 的不同儿子分支中。
d
f
s
dfs
dfs 时维护以点
x
x
x 作为根的子树中,第一个人的起点数量以及第二个人的起点数量。具体过程见代码。
#include<bits/stdc++.h>
#define ll long long
#define L(i,j,k) for(ll i=(j);i<=(k);++i)
#define R(i,j,k) for(ll i=(j);i>=(k);--i)
#define inf 9e18
#define vec vector
#define pll pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define MS(i,j) memset(i,j,sizeof (i))
const ll N=1e6+10,M=10;
const ll mod=998244353,mmod=mod-1;
const double pi=acos(-1),eps=1e-8;
using namespace std;
ll fmul(ll a,ll b){a%=mod;b%=mod;ll res=0;while(b){if(b&1){res+=a;res%=mod;}a<<=1;if(a>=mod)a%=mod;b>>=1;}return res;}
ll gcd(ll x,ll y){if(y==0) return x;return gcd(y,x%y);}
ll fksm(ll a,ll b){ll r=1;if(b<0)b+=mod-1;for(a%=mod;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}
ll lowbit(ll x){return x&(-x);}
ll dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};
ll m[5],n,t,x,y,z,l,r,u,v,k,p,pp,nx,ny,nz,ansx,ansy,mn,mx;
ll op,lim,pos,key,block;
ll cnt,tot,num,sum,ans;
ll a[N],b[N],c[10];
double dans;
bool vis[5][N],flag;
char s[N],mapp,zz[5];
struct qq{ll x,y,z;}q;
bool cmp(qq u,qq v){
return u.x>v.x;
}
bool cmp1(qq u,qq v){
return u.x<v.x;
}
bool cmpl(ll u,ll v){return u>v;}
struct cmps{bool operator()(ll u,ll v){
return u>v;
}};//shun序
pair<ll,ll>pr;
vector<qq>sv[N];
vec<ll>ss[N],vans;//v.assign(m,vector<ll>(n));
//priority_queue<ll,vector<ll>,cmps>sp;
queue<ll>sq;
stack<ll>st;
map<ll,ll>mp;
multiset<ll>se;
set<ll>::iterator it;
bitset<M>bi;
struct edge{
ll cnt,hed[N],to[N*2],nxt[N*2];
ll w[N*2];
void add(ll u,ll v,ll ew){to[++cnt]=v;w[cnt]=ew;nxt[cnt]=hed[u];hed[u]=cnt;}
void ADD(ll u,ll v,ll ew){add(u,v,ew);add(v,u,ew);}
void clear(ll n){cnt=0;L(i,1,n)hed[i]=0;}
}eg;
pll dfs(ll u,ll ac,ll i0,ll i1){
ll n0=vis[i0][u],n1=vis[i1][u],num=n0*n1;
for(ll i=eg.hed[u];i;i=eg.nxt[i]){
ll v=eg.to[i];
if(v!=ac){
pll tmp=dfs(v,u,i0,i1);
num+=n0*tmp.se+n1*tmp.fi;
n0+=tmp.fi,n1+=tmp.se;
}
}
ans-=num*b[u]*2;
return mkp(n0,n1);
}
void dfs0(ll u,ll ac){
for(ll i=eg.hed[u];i;i=eg.nxt[i]){
ll v=eg.to[i];
if(v!=ac){
b[v]=b[u]+eg.w[i];
dfs0(v,u);
}
}
}
void solve(){
scanf("%lld",&n);
L(i,1,n-1){
scanf("%lld%lld%lld",&x,&y,&z);
eg.ADD(x,y,z);
}
b[1]=0;
dfs0(1,0);
L(i,1,3){
scanf("%lld",&m[i]);
c[i]=0;
L(j,1,m[i]){
scanf("%lld",&x);
vis[i][x]=1;
c[i]+=b[x];
}
}
ans=c[1]*m[2]+c[2]*m[1];
dfs(1,0,1,2);//printf("%lld\n",ans);
double d0=(double)ans/(double)m[1]/(double)m[2];
ans=c[1]*m[3]+c[3]*m[1];
dfs(1,0,1,3);//printf("%lld\n",ans);
double d1=(double)ans/(double)m[1]/(double)m[3];
ans=c[2]*m[3]+c[3]*m[2];
dfs(1,0,2,3);//printf("%lld\n",ans);
double d2=(double)ans/(double)m[2]/(double)m[3];
dans=(d0+d1+d2)/2.0;
printf("%.10lf\n",dans);
}
int main(){
// ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
// cout<<fixed<<setprecision(12);//精度
ll Case=1;
//scanf("%lld",&Case);
while(Case--)solve();
}
5、【G. Caesar Cipher】
题目来源:
2020 CCPC 威海站 G. Caesar Cipher
难度:
银牌🥈(参照比赛正式队情况)
题意:
给定一个长度为 n n n 的序列 a a a, a i ∈ [ 0 , 65536 ) a_i∈[0,65536) ai∈[0,65536),要求支持两种操作:
- 给定一段区间 [ l , r ] [l,r] [l,r] ,对于所有 i ∈ [ l , r ] i∈[l,r] i∈[l,r], a i = ( a i + 1 ) m o d 65536 a_i=(a_i+1) \mod 65536 ai=(ai+1)mod65536,
- 给定三个整数 x , y , l x,y,l x,y,l,询问区间 [ x , x + l − 1 ] [x,x+l-1] [x,x+l−1] 与区间 [ y , y + l − 1 ] [y,y+l-1] [y,y+l−1] 是否相等。
算法:
势能线段树维护区间哈希
思路:
考虑使用线段树维护区间哈希,对于操作1,
a
i
<
65535
a_i<65535
ai<65535只需将区间哈希值加上
P
0
+
P
1
+
.
.
.
+
P
l
e
n
P^0+P^1+...+P^{len}
P0+P1+...+Plen(
l
e
n
len
len表示当前维护的区间长度),预处理
P
P
P 的前缀和即可;而对于
a
i
=
65535
a_i=65535
ai=65535,我们不能区间操作,直接递归到每个单点进行修改,操作类似势能线段树。
可以证明,对于每个节点最多会产生
8
8
8 个这样的递归操作,因此总复杂度不会超过
O
(
n
+
8
n
l
o
g
n
)
O(n+8nlogn)
O(n+8nlogn)。
#include<bits/stdc++.h>
#define ll long long
#define L(i,j,k) for(ll i=(j);i<=(k);++i)
#define R(i,j,k) for(ll i=(j);i>=(k);--i)
#define inf 9e18
#define vec vector
#define pll pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
#define MS(i,j) memset(i,j,sizeof (i))
const ll N=2e6+10,M=10;
const ll mod=998244353,mmod=mod-1,W=131;
const double pi=acos(-1),eps=1e-8;
using namespace std;
ll fmul(ll a,ll b){a%=mod;b%=mod;ll res=0;while(b){if(b&1){res+=a;res%=mod;}a<<=1;if(a>=mod)a%=mod;b>>=1;}return res;}
ll gcd(ll x,ll y){if(y==0) return x;return gcd(y,x%y);}
ll fksm(ll a,ll b){ll r=1;if(b<0)b+=mod-1;for(a%=mod;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}
ll lowbit(ll x){return x&(-x);}
ll dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};
ll m[5],n,t,x,y,z,l,r,u,v,k,p,pp,nx,ny,nz,ansx,ansy,mn,mx;
ll op,lim,pos,key,block;
ll cnt,tot,num,sum,ans;
ll a[N],b[N];
double dans;
bool vis[5][N],flag;
char s[N],mapp,zz[5];
struct qq{ll x,y,z;}q;
bool cmp(qq u,qq v){
return u.x>v.x;
}
bool cmp1(qq u,qq v){
return u.x<v.x;
}
bool cmpl(ll u,ll v){return u>v;}
struct cmps{bool operator()(ll u,ll v){
return u>v;
}};//shun序
pair<ll,ll>pr;
vector<qq>sv[N];
vec<ll>ss[N],vans;//v.assign(m,vector<ll>(n));
//priority_queue<ll,vector<ll>,cmps>sp;
queue<ll>sq;
stack<ll>st;
map<ll,ll>mp;
ll w[N],sw[N];
void init(ll n){
w[0]=1;sw[0]=0;
L(i,1,n){
w[i]=w[i-1]*W%mod;
sw[i]=(sw[i-1]+w[i-1])%mod;
}
}
struct segment{ll l,r,tag,sum,mx;}trs[N*8];
void push_up(ll k){
ll l=k*2,r=k*2+1;
trs[k].sum=(trs[l].sum*w[trs[r].r-trs[r].l+1]%mod+trs[k*2+1].sum)%mod;
trs[k].mx=max(trs[l].mx,trs[r].mx);
}
void push_down(ll k){
if(trs[k].tag){
ll l=k*2,r=k*2+1,tag=trs[k].tag;
trs[l].tag+=tag;
trs[r].tag+=tag;
trs[l].sum=(trs[l].sum+sw[trs[l].r-trs[l].l+1]*tag%mod)%mod;
trs[r].sum=(trs[r].sum+sw[trs[r].r-trs[r].l+1]*tag%mod)%mod;
trs[l].mx+=tag;
trs[r].mx+=tag;
trs[k].tag=0;
}
}
ll cal(ll l0,ll r0,ll l1,ll r1){
if(l0<l1&&r0>=l1&&r0<=r1)return r0-l1+1;
else if(l0>=l1&&l0<=r1&&r0>=l1&&r0<=r1)return r1-l1+1;
else if(l0>=l1&&l0<=r1&&r0>r1)return r1-l0+1;
else if(l0<l1&&r0>r1)return r1-l1+1;
else return 0;
}
void bd_tree(ll k,ll l,ll r){
trs[k].tag=0;
trs[k].l=l,trs[k].r=r;
if(l==r){
trs[k].sum=a[l];
trs[k].mx=a[l];
return;
}
ll mid=(l+r)/2;
bd_tree(k*2,l,mid);
bd_tree(k*2+1,mid+1,r);
push_up(k);
}
ll query(ll k,ll pl,ll pr){
if(trs[k].r<pl||trs[k].l>pr)return 0;
if(trs[k].l>=pl&&trs[k].r<=pr){
return trs[k].sum;
}
push_down(k);
ll mid=(trs[k].l+trs[k].r)/2;
if(mid>=pl&&mid+1<=pr){
ll ml=query(k*2,pl,pr),mr=query(k*2+1,pl,pr),len=cal(pl,pr,trs[k*2+1].l,trs[k*2+1].r);
return (ml*w[len]%mod+mr)%mod;
}
else if(mid>=pl){
return query(k*2,pl,pr);
}
else{
return query(k*2+1,pl,pr);
}
}
void modify(ll k,ll pl,ll pr){
if(trs[k].l==trs[k].r){
trs[k].mx=(trs[k].mx+1)%65536;
trs[k].sum=trs[k].mx;
return;
}
if(trs[k].l>=pl&&trs[k].r<=pr&&trs[k].mx<65535){
trs[k].sum=(trs[k].sum+sw[trs[k].r-trs[k].l+1])%mod;
trs[k].mx++;
trs[k].tag+=1;
return;
}
push_down(k);
ll mid=(trs[k].l+trs[k].r)/2;
if(mid>=pl)modify(k*2,pl,pr);
if(mid+1<=pr)modify(k*2+1,pl,pr);
push_up(k);
}
void solve(){
scanf("%lld%lld",&n,&t);
L(i,1,n)scanf("%lld",&a[i]);
bd_tree(1,1,n);
while(t--){
scanf("%lld%lld%lld",&op,&x,&y);
if(op==1){
modify(1,x,y);
}
else{
scanf("%lld",&k);
ll ml=query(1,x,x+k-1),mr=query(1,y,y+k-1);//printf("%lld %lld\n",ml,mr);
if(ml==mr)printf("yes\n");
else printf("no\n");
}
}
}
int main(){
// ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
// cout<<fixed<<setprecision(12);//精度
ll Case=1;
init(500000);
//scanf("%lld",&Case);
while(Case--)solve();
}