2021-11-12 部分题解

直径

solution:

首先我们知道对于树的直径的性质,那么对于两颗子树,如果将其相连变成一棵树,那么直径是两棵树直径端点的两两连边的最大值。
当我们将一颗子树从树中删去以后,那么我们就可以合并剩下的连通块,就可以利用 d f s dfs dfs序+线段树来处理了

#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define V vector<int>
using namespace std;
const int N = 2e5+10;
int n,m,now;
ll len[N],ans,dis[N];
int en[N],nex[N],lst[N],tot(1);
int sz[N],son[N],f[N],top[N],iu[N],ou[N],bl[N],cid;
int q[N];
void add(int x,int y,int z)
{en[++tot]=y;nex[tot]=lst[x];lst[x]=tot;len[tot]=z;}
void dfs(int u,int fa){
	int mx=0;sz[u]=1;
	for(int i=lst[u];i;i=nex[i]){
		int v=en[i];
		if(v==fa)continue;
		dis[v]=dis[u]+len[i];f[v]=u;
		dfs(v,u);sz[u]+=sz[v];
		if(sz[v]>mx)mx=sz[v],son[u]=v;
	}
}
void dfs1(int u,int anc){
	top[u]=anc;iu[u]=++cid;bl[cid]=u;
	if(son[u])dfs1(son[u],anc);
	for(int i=lst[u];i;i=nex[i]){
		int v=en[i];
		if(v==f[u]||v==son[u])continue;
		dfs1(v,v);
	}ou[u]=cid;
}
int lca(int u,int v){
	while(top[u]!=top[v]){
		if(dis[top[u]]<dis[top[v]])swap(u,v);
		u=f[top[u]];
	}return dis[u]<dis[v]?u:v;
}
bool cmp(int a,int b){return iu[a]<iu[b];}
V s[N];
int check(int u,int v){return (iu[u]<=iu[v]&&iu[v]<=ou[u]);}
struct node{
	int l,r;
	ll dis;
}w[N<<2];
void update(node a,node b,node &c){
	int L;ll Dis;
	c=a.dis>b.dis?a:b;
	if(a.l&&b.l){
		L=lca(a.l,b.l);Dis=dis[a.l]+dis[b.l]-2*dis[L];
		if(Dis>c.dis)c=(node){a.l,b.l,Dis};
	}if(a.l&&b.r){
		L=lca(a.l,b.r);Dis=dis[a.l]+dis[b.r]-2*dis[L];
		if(Dis>c.dis)c=(node){a.l,b.r,Dis};
	}if(a.r&&b.l){
		L=lca(a.r,b.l);Dis=dis[a.r]+dis[b.l]-2*dis[L];
		if(Dis>c.dis)c=(node){a.r,b.l,Dis};
	}if(a.r&&b.r){
		L=lca(a.r,b.r);Dis=dis[a.r]+dis[b.r]-2*dis[L];
		if(Dis>c.dis)c=(node){a.r,b.r,Dis};
	}
}
void build(int p,int l,int r){
	if(l==r){w[p]=(node){bl[l],0,0};return;}
	int mid=(l+r)>>1,ls=p<<1,rs=p<<1|1;
	build(ls,l,mid);build(rs,mid+1,r);
	update(w[ls],w[rs],w[p]);
}
node query(int p,int l,int r,int x,int y){
	if(l>=x&&r<=y){return w[p];}
	int mid=(l+r)>>1,ls=p<<1,rs=p<<1|1;
	node w1=(node){0,0,-1},w2=(node){0,0,-1},w3=(node){0,0,-1};
	if(x<=mid)w1=query(ls,l,mid,x,y);
	if(y>mid)w2=query(rs,mid+1,r,x,y);
	update(w1,w2,w3);
	return w3;
}
int st[N],ed;
signed main(){
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);add(y,x,z);
	}
	dfs(1,1);dfs1(1,1);build(1,1,cid);
	scanf("%d",&m);
	for(int i=1,k;i<=m;i++){
		scanf("%d",&k);ed=0;ll ans=0;
		for(int j=1;j<=k;j++){
			int x,y,z;scanf("%d",&x);
			y=en[x<<1];z=en[(x<<1)^1];q[j]=dis[y]>dis[z]?y:z;
		}q[++k]=1;sort(q+1,q+1+k,cmp);
		for(int j=1;j<=k;j++){
			while(ed&&!check(st[ed],q[j]))--ed;
			if(ed)s[st[ed]].push_back(q[j]);st[++ed]=q[j];
		}
		for(int j=1;j<=k;j++){
			node now=(node){0,0,-1},res1=(node){0,0,-1},res2;
			int st=iu[q[j]];
			for(int l=0;l<s[q[j]].size();l++){
				res1=query(1,1,n,st,iu[s[q[j]][l]]-1);
				res2=now;update(res1,res2,now);
				st=ou[s[q[j]][l]]+1;
			}
			res1=query(1,1,n,st,ou[q[j]]);res2=now;update(res1,res2,now);
			s[q[j]].resize(0);ans+=now.dis;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

最低位

solution:

x = = l o w b i t ( x ) x==lowbit(x) x==lowbit(x)时,我们期望操作的次数为2
每一次加或减 l o w b i t lowbit lowbit的操作都不会让 1 1 1的个数增加,所以对于一个数的清除时间复杂度时 O ( l o g ) O(log) O(log),但是我们要处理一个区间的 f ( ) f() f(),所以我们继续往下计算
首先对于奇数 2 k + 1 , f ( 2 k + 1 ) = ( f ( 2 k ) + f ( 2 k + 2 ) ) / 2 2k+1,f(2k+1)=(f(2k)+f(2k+2))/2 2k+1f(2k+1)=(f(2k)+f(2k+2))/2
对于一个偶数 2 k 2k 2k f ( 2 k ) = f ( k ) f(2k) = f(k) f(2k)=f(k)
g ( n ) = ∑ i = 1 n f ( i ) g(n)=\sum_{i=1}^{n}{f(i)} g(n)=i=1nf(i)
g ( 2 k ) = k + g ( k ) + f ( 2 k ) / 2 g(2k) =k+g(k) + f(2k)/2 g(2k)=k+g(k)+f(2k)/2
所以 g ( 2 k + 1 ) = g ( 2 k ) + f ( 2 k + 1 ) g(2k+1)=g(2k)+f(2k+1) g(2k+1)=g(2k)+f(2k+1)
递推计算一下即可

#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
//using namespace std;
const int N = 1e5+10;
const ll mo = 998244353;
ll inv,L,R,ans;
std::map<ll,ll>f,g;
ll quick_pow(ll x,ll cnt){
	ll res=1;
	for(;cnt;cnt>>=1,x=x*x%mo)if(cnt&1)res=res*x%mo;
	return res;
}
ll dfs(ll a){
	if(!a)return 0;
	if(a==(a&-a))return 2;
	if(f[a])return f[a];
	ll b=(a&-a);
	f[a]=((dfs(a-b)+dfs(a+b))%mo*inv%mo+1ll)%mo;
	return f[a];
}
ll s(ll n){
	if(!n)return 0;
	if(g[n])return g[n];
	if(n&1)g[n]=(s(n-1)+dfs(n))%mo;
	else g[n]=((n>>1ll)%mo+2ll*s(n>>1ll)%mo-dfs(n)*inv%mo)%mo;
	return g[n];
}
signed main(){
	scanf("%lld%lld",&L,&R);inv=quick_pow(2,mo-2);
	ans = (s(R)-s(L-1))%mo;
	printf("%lld\n",(ans%mo+mo)%mo);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值