20190312

第一次打字竞赛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 1n,c105

题解

最终所有的路径会交于一点,如果没有交于一点的话,交于一点更优
所以只要一个点,如果以这个点为根,最大的子树内含有的配对点数 ≤ 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 的字符串不存在长度 &gt; 1 &gt;1 >1 的回文后缀。
答案对 M M M 取模。
1 ≤ n ≤ 1 0 7 , 1 ≤ S &lt; M ≤ 2 30 − 1 1 \leq n \leq 10^7,1\leq S &lt; M \leq 2^{30}-1 1n107,1S<M2301

题解

将字符串倒着看,设 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=fi1×sf2i+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 1n,q3×105,107wi,x107

题解

没有真的换根呦
对于 2 操作,我们可以进行一下分讨
l = l c a ( u , v ) l=lca(u,v) l=lca(u,v)

  1. 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
  2. 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
  3. 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值