第一次打字竞赛QwQ
T1
内存限制:512 MiB
时间限制:1000 ms
由于 zzq 太懒了,所以这题没有题目背景。
有一棵树,树上有
n
n
n 个点,每条边上有一个非负边权。
在这
n
n
n 个点中有
k
k
k 个特殊点,其中
k
k
k 为偶数。定义两个点的距离为它们在树上的简单路径上的边权之和。你需要将这
k
k
k 个点配成
k
2
\frac{k}{2}
2k 个互不相交的对,并最大化每一对点的距离之和。
1
≤
n
,
c
≤
1
0
5
1 \leq n,c \leq 10^5
1≤n,c≤105
题解
最终所有的路径会交于一点,如果没有交于一点的话,交于一点更优
所以只要一个点,如果以这个点为根,最大的子树内含有的配对点数
≤
k
2
\leq \frac{k}{2}
≤2k ,则以这个点为根去配对即可
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,k,hd[N],nx[N*2],t,V[N*2],sz[N],ax[N],now;
bool vis[N];stack<int>g[N];
struct O{
int x,s;
friend bool operator < (const O& A,const O& B){
return A.s<B.s;
}
};
priority_queue<O>q;
void add(int u,int v){
V[++t]=v;nx[t]=hd[u];hd[u]=t;
}
void get(int x,int fa){
if (vis[x]) g[now].push(x);
for (int i=hd[x];i;i=nx[i])
if (V[i]!=fa) get(V[i],x);
}
void dfs(int x,int fa){
sz[x]=vis[x];
for (int i=hd[x];i;i=nx[i])
if (V[i]!=fa)
dfs(V[i],x),sz[x]+=sz[V[i]],
ax[x]=max(ax[x],sz[V[i]]);
ax[x]=max(ax[x],k-sz[x]);
if ((ax[x]<<1)<=k){
for (int i=hd[x];i;i=nx[i]){
now=V[i],get(V[i],x);
int s=g[V[i]].size();
if (s) q.push(O{V[i],s});
}
if (vis[x])
g[x].push(x),q.push((O){x,1});
while(!q.empty()){
int A=q.top().x;q.pop();
int B=q.top().x;q.pop();
printf("%d %d\n",g[A].top(),g[B].top());
g[A].pop();g[B].pop();
int sA=g[A].size(),sB=g[B].size();
if (sA) q.push((O){A,sA});
if (sB) q.push((O){B,sB});
}
exit(0);
}
}
int main(){
scanf("%d%d",&n,&k);
for (int x,y,z,i=1;i<n;i++)
scanf("%d%d%d",&x,&y,&z),
add(x,y),add(y,x);
for (int x,i=1;i<=k;i++)
scanf("%d",&x),vis[x]=1;
dfs(1,0);return 0;
}
T2
内存限制:256 MiB
时间限制:1000 ms
给定字符集大小
S
S
S ,问有多少个长度为
N
N
N 的字符串不存在长度
>
1
>1
>1 的回文后缀。
答案对
M
M
M 取模。
1
≤
n
≤
1
0
7
,
1
≤
S
<
M
≤
2
30
−
1
1 \leq n \leq 10^7,1\leq S < M \leq 2^{30}-1
1≤n≤107,1≤S<M≤230−1
题解
将字符串倒着看,设
f
i
f_i
fi 表示前
i
i
i 位且满足条件的字符串的方案数
则
f
i
=
f
i
−
1
×
s
−
f
i
+
1
2
f_i=f_{i-1} \times s - f_{\frac{i+1}{2}}
fi=fi−1×s−f2i+1
(前
i
+
1
2
\frac{i+1}{2}
2i+1 不为回文,将其反过来为回文
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e7+5;
LL s,m,f[N];int n;
int main(){
scanf("%d%lld%lld",&n,&s,&m);
f[0]=1;for (int i=1;i<=n;i++)
f[i]=(f[i-1]*s-f[(i+1)/2])%m;
return printf("%lld\n",f[n]),0;
}
T3
内存限制:512 MiB
时间限制:1000 ms
有一棵
n
n
n 个点的树,每个节点上有一个权值
w
i
w_i
wi ,最开始根为
1
1
1 号点。现在有
3
3
3 种类型的操作:
· 1
r
o
o
t
root
root , 表示将根设为
r
o
o
t
root
root 。
· 2
u
u
u
v
v
v
x
x
x , 设
u
,
v
u,v
u,v 的最近公共祖先为
p
p
p , 将
p
p
p 的子树中的所有点的权值加上
x
x
x 。
· 3
u
u
u ,查询
u
u
u 的子树中的所有点的权值和。
对于每个 3
操作,输出答案。
1
≤
n
,
q
≤
3
×
1
0
5
,
−
1
0
7
≤
w
i
,
x
≤
1
0
7
1 \leq n,q \leq 3 \times 10^5,-10^7 \leq w_i,x \leq 10^7
1≤n,q≤3×105,−107≤wi,x≤107
题解
没有真的换根呦
对于 2
操作,我们可以进行一下分讨
设
l
=
l
c
a
(
u
,
v
)
l=lca(u,v)
l=lca(u,v)
-
u
u
u 在
r
o
o
t
root
root 子树内,
v
v
v 在
r
o
o
t
root
root 子树内
如果 l = r t l=rt l=rt 就是全部 + x +x +x ,否则就是 l l l 子树 + x +x +x -
u
u
u 在
r
o
o
t
root
root 子树内,
v
v
v 不在
r
o
o
t
root
root 子树内/
u
u
u 不在
r
o
o
t
root
root 子树内,
v
v
v 在
r
o
o
t
root
root 子树内
全部 + x +x +x -
u
u
u 不在
r
o
o
t
root
root 子树内,
v
v
v 不在
r
o
o
t
root
root 子树内
如果 l l l 不为 r o o t root root 的祖先,就是 l l l 子树 + x +x +x
否则先全部 + x +x +x ,然后找到 r o o t root root 到 l l l 的路径上最后一个不在 u u u 到 v v v 这条路径上的点 w w w , w w w 子树 + x +x +x
对于3
操作进行类似的分讨,即可过题
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=3e5+5;
int rt,q,w[N],n,c,f[N*2][22],hd[N],nx[N*2],V[N*2],t,tt,id[N],dfn[N],e[N],d[N],ot[N],lg[N*2],fa[N][22];
LL s[N*4],ans,tg[N*4];
void add(int u,int v){
V[++t]=v;nx[t]=hd[u];hd[u]=t;
}
void dfs(int x,int F){
f[++c][0]=x;d[x]=d[F]+1;fa[x][0]=F;
for (int i=1;fa[fa[x][i-1]][i-1];i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
dfn[id[x]=++tt]=x;e[x]=c;
for (int i=hd[x];i;i=nx[i])
if (V[i]!=F)
dfs(V[i],x),f[++c][0]=x;
ot[x]=tt;
}
int lca(int l,int r){
if (l>r) swap(l,r);int i=log2(r-l+1);
if (d[f[l][i]]<d[f[r-(1<<i)+1][i]])
return f[l][i];
return f[r-(1<<i)+1][i];
}
bool pd(int x,int y){
return (id[x]>=id[y] && id[x]<=ot[y]);
}
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
void pushdown(int k,int l,int r){
LL v=tg[k];tg[k]=0;
s[Ls]+=v*(mid-l+1);tg[Ls]+=v;
s[Rs]+=v*(r-mid);tg[Rs]+=v;
}
void build(int k,int l,int r){
if (l==r) s[k]=w[dfn[l]];
else
build(Ls,l,mid),
build(Rs,mid+1,r),
s[k]=s[Ls]+s[Rs];
}
void update(int k,int l,int r,int L,int R,int v){
if (L<=l && r<=R){
s[k]+=1ll*v*(r-l+1);tg[k]+=v;return;
}
if (tg[k]) pushdown(k,l,r);
if (mid>=L) update(Ls,l,mid,L,R,v);
if (mid<R) update(Rs,mid+1,r,L,R,v);
s[k]=s[Ls]+s[Rs];
}
LL query(int k,int l,int r,int L,int R){
if (L<=l && r<=R) return s[k];
if (tg[k]) pushdown(k,l,r);
if (mid<L) return query(Rs,mid+1,r,L,R);
else if (mid>=R) return query(Ls,l,mid,L,R);
return query(Rs,mid+1,r,L,R)+query(Ls,l,mid,L,R);
}
int main(){
for (int i=0;i<20;i++) lg[1<<i]=i;
scanf("%d%d",&n,&q);rt=1;
for (int i=1;i<=n;i++)
scanf("%d",&w[i]);
for (int x,y,i=1;i<n;i++)
scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs(1,0);build(1,1,n);
for (int i=c;i;i--)
for (int j=1;i+(1<<j)<=c+1;j++)
if (d[f[i][j-1]]<d[f[i+(1<<(j-1))][j-1]])
f[i][j]=f[i][j-1];
else f[i][j]=f[i+(1<<(j-1))][j-1];
for (int op,u,v,l,x;q--;){
scanf("%d",&op);
if (op<2) scanf("%d",&rt);
else if (op>2){
scanf("%d",&l);
if (pd(l,rt)){
if (l==rt) ans=query(1,1,n,1,n);
else ans=query(1,1,n,id[l],ot[l]);
}
else{
if (lca(e[l],e[rt])==l){
ans=query(1,1,n,1,n);
l=d[rt]-d[l]-1;v=rt;
for (;l;l-=l&-l) v=fa[v][lg[l&-l]];
ans-=query(1,1,n,id[v],ot[v]);
}
else ans=query(1,1,n,id[l],ot[l]);
}
printf("%lld\n",ans);
}
else{
scanf("%d%d%d",&u,&v,&x);
if (pd(u,rt)){
if (pd(v,rt)){
l=lca(e[u],e[v]);
if (l==rt) update(1,1,n,1,n,x);
else update(1,1,n,id[l],ot[l],x);
}
else update(1,1,n,1,n,x);
}
else{
if (pd(v,rt)) update(1,1,n,1,n,x);
else{
l=lca(e[u],e[v]);
if (pd(rt,l)){
update(1,1,n,1,n,x);l=rt;
for (int i=19;~i;i--)
if (fa[l][i] && !pd(u,fa[l][i]) && !pd(v,fa[l][i]))
l=fa[l][i];
update(1,1,n,id[l],ot[l],-x);
}
else update(1,1,n,id[l],ot[l],x);
}
}
}
}
return 0;
}