2024年7月杂题乱写

7.3

P4178 Tree

点分治模板,套一个树状数组查询即可。
如果树状数组需要支持对下标 \(0\) 的操作,可以这么写。
p|=p+1 找父亲 p=(p&(p+1))-1 找上一个控制范围
image

点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=4e4+10;
int n,m,head[N],d[N],tot,dis[N],k,rt,t[N],ans,zcnode[N],tt,size[N],top;
bool vis[N];
struct EDGE{int v,next,w;}e[N<<1];
inline void add(int u,int v,int w){e[++tot]={v,head[u],w};head[u]=tot;}
inline void insert(int p){for(;p<=k;p|=p+1)t[p]++;}
inline int query(int p){if(p<0)return 0;int res=0;for(;p>=0;p=(p&(p+1))-1)res+=t[p];return res;}
inline void del(int p){for(;p<=k;p|=p+1)t[p]=0;}
inline void findrt(int x,int fa,int cnt){
	size[x]=1;int mx=0;
	for(int i=head[x],y;i;i=e[i].next){
		if(vis[y=e[i].v]||y==fa)continue;
		findrt(y,x,cnt);
		size[x]+=size[y];
		mx=std::max(mx,size[y]);
	}
	mx=std::max(mx,cnt-size[x]);
	if(mx<=cnt/2)rt=x;
}
inline void calc(int x,int fa){
	d[++top]=x;zcnode[++tt]=x;
	for(int i=head[x],y;i;i=e[i].next){
		if(vis[y=e[i].v]||fa==y)continue;
		dis[y]=dis[x]+e[i].w;
		calc(y,x);
	}
}
inline void solve(int x){
	vis[x]=1;insert(0);
	for(int i=head[x],y;i;i=e[i].next){
		if(vis[y=e[i].v])continue;
		dis[y]=e[i].w;
		calc(y,x);
		for(int j=1;j<=top;++j){
			ans+=query(k-dis[d[j]]);
		}
		for(int j=1;j<=top;++j)insert(dis[d[j]]);
		top=0;
	}
	del(0);
	for(int i=1;i<=tt;++i)del(dis[zcnode[i]]);
	tt=0;
	for(int i=head[x],y;i;i=e[i].next){
		if(vis[y=e[i].v])continue;
		findrt(y,x,size[y]);
		findrt(rt,0,size[y]);
		solve(rt);
	}
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read(),w=read();
		add(u,v,w);add(v,u,w);
	}
	k=read();
	findrt(1,0,n);findrt(rt,0,n);
	solve(rt);
	std::cout<<ans<<'\n';
}

P4149 [IOI2011] Race

也是板子,直接开个桶统计一下距离 \(dis\) 的最小边数,然后及时更新答案就行。(注意开大栈)

点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=1e6+10;
int n,k,head[N],d[N],dis[N],top,tot,dep[N],size[N],rt,ans,minn[N];
bool vis[N];
struct EDGE{int v,next,w;}e[N<<1];
inline void add(int u,int v,int w){e[++tot]={v,head[u],w};head[u]=tot;}
inline void findrt(int x,int fa,int cnt){
	size[x]=1;int mx=0;
	for(int i=head[x],y;i;i=e[i].next){
		if(vis[y=e[i].v]||y==fa)continue;
		findrt(y,x,cnt);
		size[x]+=size[y];
		mx=std::max(mx,size[y]);
	}
	mx=std::max(mx,cnt-size[x]);
	if(mx<=cnt/2)rt=x;
}
inline void calc(int x,int fa){
	d[++top]=x;
	for(int i=head[x],y;i;i=e[i].next){
		if(vis[y=e[i].v]||y==fa)continue;
		dep[y]=dep[x]+1;
		dis[y]=dis[x]+e[i].w;
		calc(y,x);
	}
}
inline void solve(int x){
	vis[x]=1;minn[0]=0;
	std::vector<int> zcnode;
	for(int i=head[x],y;i;i=e[i].next){
		if(vis[y=e[i].v])continue;
		dis[y]=e[i].w;dep[y]=1;
		calc(y,x);
		for(int j=1;j<=top;++j){
			int zc=k-dis[d[j]];
			if(zc<0||minn[zc]<0)continue;
			if(ans<0)ans=minn[zc]+dep[d[j]];else ans=std::min(ans,minn[zc]+dep[d[j]]);
		}
		for(int j=1;j<=top;++j){
			int val=dis[d[j]];
			if(val>k)continue;
			if(minn[val]<0)minn[val]=dep[d[j]];else minn[val]=std::min(minn[val],dep[d[j]]);
			zcnode.push_back(val);
		}
		top=0;
	}
	for(int it:zcnode)minn[it]=-1;
	zcnode.clear();
	for(int i=head[x],y;i;i=e[i].next){
		if(vis[y=e[i].v])continue;
		findrt(y,x,size[y]);
		findrt(rt,0,size[y]);
		solve(rt);
	}
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read();k=read();ans=-1;memset(minn,-1,sizeof(minn));
	for(int i=1;i<n-1;++i){
		int u=read()+1,v=read()+1,w=read();
		add(u,v,w),add(v,u,w);
	}
	findrt(1,0,n),
	findrt(rt,0,n);
	solve(rt);
	std::cout<<ans<<'\n';
}

7.5

CF293E Close Vertices

相比于模板,这道题只多了一个路径长度不超过 \(l\) 的限制,自然可以想到二维数点。
点分治一般有两种写法,一种运用容斥,一种不运用,因为树状数组二维数点只能离线,所以只能运用容斥,否则不能将询问离线下来,或者可以采用树套树来维护,这样可以不用容斥。
具体来说,每次分治时,将路径权值固定,从小到大排序,然后将路径长度作为树状数组的下标,直接修改查询,然后容斥减去子树内的贡献即可。每次分治结束时将树状数组清空。
由于长度和权值会有 \(0\),可以将树状数组这样写:

  • p|=p+1 查找父亲。
  • p=(p&p+1)-1 查找上一个范围的父亲。

或者直接全部加一后存也行。

点击查看代码
#include<bits/stdc++.h>
typedef long long ll;
#define int long long
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=1e5+10;
int n,l,k,size[N],head[N],dis[N],top,tot,dep[N],rt,t[N],ans;
std::vector<std::pair<int,int>> d;
bool vis[N];
struct EDGE{int v,next,w;}e[N<<1];
inline void add(int u,int v,int w){e[++tot]={v,head[u],w};head[u]=tot;}
inline void insert(int p,int x){for(;p<=n;p|=p+1)t[p]+=x;}
inline int query(int p){int res=0;for(;p>=0;p=(p&p+1)-1)res+=t[p];return res;}
inline void findrt(int x,int fa,int cnt){
	size[x]=1;int mx=0;
	for(int i=head[x],y;i;i=e[i].next){
		if(vis[y=e[i].v]||y==fa)continue;
		findrt(y,x,cnt);
		size[x]+=size[y];
		mx=std::max(size[y],mx);
	}
	mx=std::max(mx,cnt-size[x]);
	if(mx<=cnt/2)rt=x;
}
inline void dfs(int x,int fa,int di,int de){
	d.push_back({di,de});
	for(int i=head[x],y;i;i=e[i].next){
        if(vis[y=e[i].v]||y==fa)continue;
        dfs(y,x,di+e[i].w,de+1);
    }
}
inline int calc(int x,int fa,int di,int de){
	dfs(x,fa,di,de);int res=0;
	std::vector<std::pair<int,int>>q;
	std::sort(d.begin(),d.end());
	for(int i=d.size()-1;i>=0;i--){q.push_back({k-d[i].first,l-d[i].second});}
	int l=0;
	for(auto it:q){
		while(l<d.size()&&d[l].first<=it.first)insert(d[l].second,1),l++;
		res+=query(it.second);
	}
	for(int i=0;i<l;++i)insert(d[i].second,-1);
	d.clear();
	return res;
}
inline void solve(int x){
	vis[x]=1;ans+=calc(x,0,0,0);
	for(int i=head[x],y;i;i=e[i].next){
		if(vis[y=e[i].v])continue;
		ans-=calc(y,0,e[i].w,1);
	}
	for(int i=head[x],y;i;i=e[i].next){
        if(vis[y=e[i].v])continue;
		findrt(y,0,size[y]);findrt(rt,0,size[y]);
		solve(rt);
    }
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read(),l=read(),k=read();
	for(int i=1;i<n;++i){
		int x=read(),w=read();
		add(x,i+1,w),add(i+1,x,w);
	}
	findrt(1,0,n);findrt(rt,0,n);
	solve(rt);
	std::cout<<(ans-n)/2<<'\n';
}
有一说一,这个机房也太吵了。😡😡

7.9

CF715C

记一下每个点为起点和终点对 \(M\) 取模的值,发现分子树不好统计,所以直接容斥做完了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=1e5+10;
int n,mod,head[N],tot,size[N],d[N],top,rt,_p[N],ny[N];
ll ans;
std::pair<int,int> disst[N],disen[N];
std::map<int,int> mpst;
bool vis[N];
inline void exgcd(int a,int b,int &x,int &y){
    if(!b){x=1,y=0;return ;}
    exgcd(b,a%b,x,y);
    int t=x;x=y;y=t-a/b*y;
}
inline int inv(int a,int b){int x=0,y=0;exgcd(a,b,x,y);return (x%b+b)%b;}
struct EDGE{int v,next,w;}e[N<<1];
inline void add(int u,int v,int w){e[++tot]={v,head[u],w};head[u]=tot;}
inline void findrt(int x,int fa,int cnt){
    size[x]=1;int mx=0;
    for(int i=head[x],y;i;i=e[i].next){
        if(vis[y=e[i].v]||fa==y)continue;
        findrt(y,x,cnt);
        size[x]+=size[y];
        mx=std::max(mx,size[y]);
    }
    mx=std::max(mx,cnt-size[x]);
    if(mx<=cnt/2){rt=x;}
}
inline void dfs(int x,int fa,std::pair<int,int> st,std::pair<int,int> en){
    d[++top]=x;
    mpst[st.first]++;
    for(int i=head[x],y;i;i=e[i].next){
        if(vis[y=e[i].v]||fa==y)continue;
        int a=st.first,b=st.second;
        disst[y]={(e[i].w*_p[b]+a)%mod,b+1};
        a=en.first,b=en.second;
        disen[y]={(e[i].w+a*10)%mod,b+1};
        dfs(y,x,disst[y],disen[y]);
    }   
}
inline int calc(int x,std::pair<int,int> st,std::pair<int,int> en){
    dfs(x,-1,st,en);
    int res=0;
    for(int i=1;i<=top;++i){
        x=d[i];
        int a=disen[x].first,b=disen[x].second;
        int zc=(mod-a)*ny[b]%mod;
        res+=mpst[zc];
    }
    top=0;mpst.clear();
    return res;
}
inline void solve(int x){
    vis[x]=1;
    ans+=calc(x,disst[x]={0,0},disen[x]={0,0});
    for(int i=head[x],y;i;i=e[i].next){
        if(vis[y=e[i].v])continue;
        ans-=calc(y,disst[y]={e[i].w%mod,1},disen[y]={e[i].w%mod,1});
    }
    for(int i=head[x],y;i;i=e[i].next){
        if(vis[y=e[i].v])continue;
        findrt(y,-1,size[y]);findrt(rt,-1,size[y]);
        solve(rt);
    }
}
signed main(){
    std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    n=read(),mod=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read(),w=read();
        add(u,v,w);add(v,u,w);
    }
    _p[0]=1;ny[0]=1;int kk=inv(10,mod);
    for(int i=1;i<=n;++i)_p[i]=_p[i-1]*10%mod,ny[i]=ny[i-1]*kk%mod;
    findrt(1,-1,n);findrt(rt,-1,n);
    solve(rt);
    std::cout<<ans-n<<'\n';
}

P4200 千山鸟飞绝

虽然这题可以直接平衡树 tag 修改统计,但是 wtcl,所以我写了平衡树加单调栈。
对于每一个位置建一棵平衡树,用来维护 \(size,val\),需要历史信息来更新答案,我一开始写了线段树,卡空间了。
由于只需要用后缀来更新,所以可以使用单调栈来维护。
比较ex的是这题不能统计自己,所以有一些特殊情况需要特殊处理,比如如果最大值是自己,往前查和往后查插入的最大值。
还有如果要飞回本来的位置,直接跳过,不更新。

点击查看代码
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=3.3e5+10;
int w[N],n,tot,id[N],maxsize[N],maxans[N],st[N];
std::vector<std::pair<int,int>> qsize[N],qval[N],qans[N],qmax[N];
std::multiset<int> s[N];
std::map<std::pair<int,int>,int> mp;
inline int max(int x){return s[x].size()?*s[x].rbegin():0;}
inline void inssize(int x,int size,int t){
    int tail=qsize[x].size()-1,head=0;
    while(head<=tail&&qsize[x][tail].first<=size){qsize[x].pop_back();tail--;}
    qsize[x].push_back({size,t});
}
inline void insans(int x,int ans,int t){
    int tail=qans[x].size()-1,head=0;
    while(head<=tail&&qans[x][tail].first<=ans){qans[x].pop_back();tail--;}
    qmax[x].push_back({ans,t});
    qans[x].push_back({ans,t});
}
inline void insval(int x,int val,int t){
    int tail=qval[x].size()-1,head=0;
    while(head<=tail&&qval[x][tail].first<=val){qval[x].pop_back();tail--;}
    qval[x].push_back({val,t});
}
inline int querysize(int x,int t){
    int l=0,r=qsize[x].size()-1,ans=-1;
    while(l<=r){
        int mid=l+r>>1;
        if(qsize[x][mid].second>=t)ans=mid,r=mid-1;
        else l=mid+1;
    }
    if(ans<0)return 0;
    return qsize[x][ans].first;
}
inline int queryval(int x,int t){
    int l=0,r=qval[x].size()-1,ans=-1;
    while(l<=r){
        int mid=l+r>>1;
        if(qval[x][mid].second>=t)ans=mid,r=mid-1;
        else l=mid+1;
    }
    if(ans<0)return 0;
    return qval[x][ans].first;
}
inline void insert(int x,int t){
    int bl=id[x];
    s[bl].insert(w[x]);
    int size=s[bl].size(),maxx=max(bl);
    inssize(bl,size,t);
    insans(bl,maxx,t);
    insval(bl,w[x],t);
}
inline void del(int x,int t){
    int bl=id[x];
    s[bl].erase(w[x]);
    int size=s[bl].size(),maxx=max(bl);
    inssize(bl,size,t);
    insans(bl,maxx,t);
    size=querysize(bl,st[x]);
    maxsize[x]=std::max(maxsize[x],size-1);
    int l=0,r=qans[bl].size()-1,ans=-1;
    while(l<=r){
        int mid=l+r>>1;
        if(qans[bl][mid].second>=st[x])ans=mid,r=mid-1;
        else l=mid+1;
    }
    if(ans<0)maxx=0;else maxx=qans[bl][ans].first;
    if(maxx!=w[x]){maxans[x]=std::max(maxx,maxans[x]);return;}
    l=0,r=qmax[bl].size()-1,ans=-1;
    while(l<=r){
        int mid=l+r>>1;
        if(qmax[bl][mid].second>=st[x])ans=mid,r=mid-1;
        else l=mid+1;
    }
    if(ans>0)maxx=qmax[bl][ans-1].first;else maxx=0;
    maxx=std::max(maxx,queryval(bl,st[x]+1));
    maxans[x]=std::max(maxx,maxans[x]);
}
int Time=0;
signed main(){
    // freopen("in.in","r",stdin);freopen("out.out","w",stdout);
    std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    n=read();
    for(int i=1;i<=n;++i){
        w[i]=read();int x=read(),y=read();
        if(!mp[{x,y}])mp[{x,y}]=++tot;
        id[i]=mp[{x,y}];
        insert(i,++Time);
        st[i]=i;
    }
    int q=read();
    for(int i=1;i<=q;++i){
        int c=read(),x=read(),y=read();
        if(!mp[{x,y}])mp[{x,y}]=++tot;
        del(c,++Time);
        id[c]=mp[{x,y}];
        insert(c,st[c]=++Time);
    }
    for(int i=1;i<=n;++i){
        del(i,++Time);
        std::cout<<1ll*maxsize[i]*maxans[i]<<'\n';
    }
}
posted @ 2024-07-04 07:30  Ishar-zdl  阅读(42)  评论(0)    收藏  举报