csp-s模拟45,50 weight,蔬菜,联盟

题面:https://www.cnblogs.com/Juve/articles/11574985.html

蔬菜:

题解说和四维偏序有关,但是看到它没有修改,可以莫队水过

一个二维莫队,定义四个指针,分别表示左上角和右下角的坐标

然后模拟序列上的莫队移动

while(u<ask[i].x) del_h(u++);
while(u>ask[i].x) add_h(--u);
while(d<ask[i].p) add_h(++d);
while(d>ask[i].p) del_h(d--);
while(l<ask[i].y) del_l(l++);
while(l>ask[i].y) add_l(--l);
while(r<ask[i].q) add_l(++r);
while(r>ask[i].q) del_l(r--);

u是up,d是down,l是left,r是right,然后统计答案即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define int long long
#define re register
using namespace std;
const int MAXN=205;
int n,m,q,a[MAXN][MAXN],cnt=0,tot=0,ans[100005];
int b[MAXN*MAXN],num[MAXN*MAXN],u=1,d=0,l=1,r=0;
struct node{
	int x,y,p,q,id;
	friend bool operator < (node a,node b){
		return (a.x==b.x?(a.y==b.y?(a.p==b.p?(a.q<b.q):a.p<b.p):a.y<b.y):a.x<b.x);
	}
}ask[100005];
void del_h(int ha){
	for(int i=l;i<=r;++i) --num[a[ha][i]];
}
void add_h(int ha){
	for(int i=l;i<=r;++i) ++num[a[ha][i]];
}
void del_l(int li){
	for(int i=u;i<=d;++i) --num[a[i][li]];
}
void add_l(int li){
	for(int i=u;i<=d;++i) ++num[a[i][li]];
}
signed main(){
	//freopen("test.in","r",stdin);
	//freopen("vio.out","w",stdout);
	scanf("%lld%lld%lld",&n,&m,&q);
	for(re int i=1;i<=n;++i){
		for(re int j=1;j<=m;++j){
			scanf("%lld",&a[i][j]);
			b[++tot]=a[i][j];
		}
	}
	sort(b+1,b+tot+1);
	cnt=unique(b+1,b+tot+1)-b-1;
	for(re int i=1;i<=n;++i){
		for(re int j=1;j<=m;++j)
			a[i][j]=lower_bound(b+1,b+cnt+1,a[i][j])-b;
	}
	for(int i=1;i<=q;++i){
		scanf("%lld%lld%lld%lld",&ask[i].x,&ask[i].y,&ask[i].p,&ask[i].q);
		ask[i].id=i;
	}
	sort(ask+1,ask+q+1);
	for(int i=1;i<=q;++i){
		while(u<ask[i].x) del_h(u++);
		while(u>ask[i].x) add_h(--u);
		while(d<ask[i].p) add_h(++d);
		while(d>ask[i].p) del_h(d--);
		while(l<ask[i].y) del_l(l++);
		while(l>ask[i].y) add_l(--l);
		while(r<ask[i].q) add_l(++r);
		while(r>ask[i].q) del_l(r--);
		for(int j=1;j<=cnt;++j){
			ans[ask[i].id]+=num[j]*num[j];
		}
	}
	for(int i=1;i<=q;++i){
		printf("%lld\n",ans[i]);
	}
	return 0;
}

联盟:

这道题和树的直径有关

先求出原树的直径,然后枚举断开的直径上的边(段直径上的边一定最优)

设断开之后两个联通块的直径分别是$l_1$,$l_2$,那么第一问答案就是

$max(l_1,l_2,\lceil\frac{l_1}{2}\rceil+\lceil\frac{l_2}{2}\rceil+1)$

第一问出来了第二问就不是问题了

然后第三问,我们随便找出一个要断的边,连接的点就是两个联通块的直径的中点

打了4个dfs和4个bfs,码长4.0k,好像我的做法很麻烦?

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define re register
using namespace std;
const int MAXN=3e5+5;
int n,fmx=0,smx=0,ans=0x3f3f3f3f;
int to[MAXN<<1],nxt[MAXN<<1],pre[MAXN],cnt=1,id[MAXN<<1],fr[MAXN<<1];
inline void add(re int u,re int v,re int rk){
	++cnt,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt,id[cnt]=rk,fr[cnt]=u;
}
int dp[2][MAXN];
int DFs(int x,int fa,int flag){
	int maxx=0;
	dp[flag][x]=0;
	for(int i=pre[x];i;i=nxt[i]){
		int y=to[i];
		if(y==fa) continue;
		int p=DFs(y,x,flag);
		dp[flag][x]=max(dp[flag][x],max(maxx+p,dp[flag][y]));
		maxx=max(maxx,p);
	}
	return maxx+1;
}
int d[MAXN],p[MAXN],qd,zd,dis[MAXN];
int stac[MAXN],topc=0;
int bfs(int s){
	topc=0;
	memset(d,0x3f,sizeof(d));
	d[s]=0;
	p[s]=0;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=pre[x];i;i=nxt[i]){
			int y=to[i];
			if(d[y]==0x3f3f3f3f){
				d[y]=d[x]+1;
				p[y]=i;
				q.push(y);
			}
		}
	}
	int y=1;
	for(int i=1;i<=n;++i){
		if(dis[i]==0x3f3f3f3f) continue;
		if(d[i]>d[y]) y=i,topc=0;
		//if(d[i]==d[y]) stac[++topc]=i;
	}
	return y;
}
int BFS(int s,int ed){
	if(s==ed) return 0;
	memset(dis,0x3f,sizeof(dis));
	dis[s]=0;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=pre[x];i;i=nxt[i]){
			int y=to[i];
			if(y==ed) continue;
			if(dis[y]==0x3f3f3f3f){
				dis[y]=dis[x]+1;
				q.push(y);
			}
		}
	}
	int res=0;
	for(int i=1;i<=n;++i){
		if(dis[i]==0x3f3f3f3f) continue;
		res=max(res,dis[i]);
	}
	return res;
}
int sta[MAXN],top=0;
/*void work(int x){
	if(x==0) return ;
	int i=p[x];
	int p=BFS(zd,fr[i]),q=BFS(qd,x);
	//cout<<x<<' '<<fr[i]<<' '<<p<<' '<<q<<' '<<' '<<(p+1)/2+(q+1)/2+1<<' '<<i<<endl;
	int t=max(max(p,q),(p+1)/2+(q+1)/2+1);
	if(ans==t) sta[++top]=i;
	if(ans>t) top=0,sta[++top]=i;
	ans=min(ans,t);
	work(fr[i]);
}*/
void work(int x){
	if(x==0) return ;
	int i=p[x];
	int p=dp[1][fr[i]],q=dp[0][x];
	//cout<<x<<' '<<fr[i]<<' '<<p<<' '<<q<<' '<<' '<<(p+1)/2+(q+1)/2+1<<' '<<i<<endl;
	int t=max(max(p,q),(p+1)/2+(q+1)/2+1);
	if(ans==t) sta[++top]=i;
	if(ans>t) top=0,sta[++top]=i;
	ans=min(ans,t);
	work(fr[i]);
}
int Bfs(int s){
	memset(d,0x3f,sizeof(d));
	d[s]=0;
	p[s]=0;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=pre[x];i;i=nxt[i]){
			int y=to[i];
			if(d[y]==0x3f3f3f3f){
				d[y]=d[x]+1;
				p[y]=i;
				q.push(y);
			}
		}
	}
	int y=1;
	for(int i=1;i<=n;++i){
		if(dis[i]==0x3f3f3f3f) continue;
		if(d[i]>d[y]) y=i;
	}
	return y;
}
int edge,frr,too,len;
int BFs(int s,int ed){
	if(s==ed) return 0;
	memset(dis,0x3f,sizeof(dis));
	dis[s]=0;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=pre[x];i;i=nxt[i]){
			int y=to[i];
			if(y==ed) continue;
			if(dis[y]==0x3f3f3f3f){
				dis[y]=dis[x]+1;
				p[y]=i;
				q.push(y);
			}
		}
	}
	int y=s;
	for(int i=1;i<=n;++i){
		if(dis[i]==0x3f3f3f3f) continue;
		if(dis[i]>dis[y]) y=i;
	}
	return y;
}
int ans1=0,ans2=0;
void dfs(int x,int l){
	if(x==0) return ;
	if(l==len){
		ans1=x;
		return ;
	}
	int i=p[x];
	dfs(fr[i],l+1);
}
void DFS(int x,int l){
	if(x==0) return ;
	if(l==len){
		ans2=x;
		return ;
	}
	int i=p[x];
	DFS(fr[i],l+1);
}
signed main(){
	//freopen("dt.in","r",stdin);
	//freopen("my.out","w",stdout);
	scanf("%d",&n);
	for(int i=1,u,v;i<n;++i){
		scanf("%d%d",&u,&v);
		add(u,v,i),add(v,u,i);
	}
	qd=bfs(1);
	zd=bfs(qd);
	DFs(qd,0,0);
	DFs(zd,0,1);
	work(zd);
	printf("%d\n",ans);
	sort(sta+1,sta+top+1);
	top=unique(sta+1,sta+top+1)-sta-1;
	printf("%d ",top);
	for(int i=1;i<=top;++i){
		printf("%d ",id[sta[i]]);
	}
	puts("");
	edge=sta[top];
	frr=fr[edge],too=to[edge];
	qd=BFs(frr,too);
	zd=BFs(qd,too);
	len=dis[zd]/2;
	//cout<<len<<endl;
	dfs(zd,0);
	qd=BFs(too,frr);
	zd=BFs(qd,frr);
	len=dis[zd]/2;
	DFS(zd,0);
	printf("%d %d %d %d\n",frr,too,ans1,ans2);
	return 0;
}

weight:

先kruskal跑出最小生成树,然后对于树边和非树边进行讨论

对于一条非树边,我们至少要将它的权值调整到树上这两个端点对应路径边权最大值 -1 才可以,

否则我们一定可以不选这条边。显然,我们调整到这么大也足够了。

对于一条树边,我们关心的显然是两个端点对应的简单路径经过这条树边的那些边,

我们最大的可能选择是那些边中权值最小的边的权值 -1, (否则我们可以选那条最小边而不选这条树边),

而我们如果将这条树边的边权调整成那个值,它也一定还会在最小生成树中。

然后我们对于最小生成树进行树剖来实现操作

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1e6+5;
const int inf=0x3f3f3f3f;
int n,m,xkl,ans[MAXN];
struct EDGE{
	int fr,to,nxt,val,id;
	bool flag;
	friend bool operator < (EDGE a,EDGE b){
		return a.val<b.val;
	}
}ed[MAXN];
int cnt=0,pre[MAXN],head[MAXN],tot=0;
void add(int u,int v,int w,int id){
	ed[++cnt]=(EDGE){u,v,pre[u],w,id,0};
	pre[u]=cnt;
}
struct node{
	int fr,to,nxt,val,id;
}e[MAXN];
void ADD(int u,int v,int val,int id){
	e[++tot]=(node){u,v,head[u],val,id};
	head[u]=tot;
}
int fa[MAXN];
int find(int x){
	return fa[x]=(fa[x]==x?x:find(fa[x]));
}
void kruskal(){
	for(int i=1;i<=n;++i) fa[i]=i;
	sort(ed+1,ed+cnt+1);
	int sum=0;
	for(int i=1;i<=cnt;++i){
		int x=find(ed[i].fr),y=find(ed[i].to);
		if(x!=y){
			fa[x]=y;
			++sum;
			ed[i].flag=1;
			ADD(ed[i].fr,ed[i].to,ed[i].val,ed[i].id);
			ADD(ed[i].to,ed[i].fr,ed[i].val,ed[i].id);
			if(sum==n-1) break;
		}
	}
}
int deep[MAXN],size[MAXN],son[MAXN],val[MAXN],rcd[MAXN];
void dfs(int x,int father){
	size[x]=1;
    for(int i=head[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==father) continue;
        deep[y]=deep[x]+1;
        fa[y]=x;
        val[y]=e[i].val;
        rcd[y]=e[i].id;
        dfs(y,x);
        size[x]+=size[y];
        if(size[y]>size[son[x]]) son[x]=y;
    }
}
int top[MAXN],id[MAXN],rk[MAXN],dfs_order=0;
void DFS(int x,int chain){
	top[x]=chain;
    id[x]=++dfs_order;
    rk[dfs_order]=x;
    if(son[x]) DFS(son[x],chain);
    for(int i=head[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa[x]||y==son[x]) continue;
        DFS(y,y);
    }
}
struct TREE{
	int l,r,laz,mx,mn;
}tr[MAXN<<2];
void build(int k,int l,int r){
	tr[k].l=l,tr[k].r=r,tr[k].laz=inf;
	if(l==r){
		tr[k].mn=inf;
		tr[k].mx=val[rk[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid),build(k<<1|1,mid+1,r);
	tr[k].mx=max(tr[k<<1].mx,tr[k<<1|1].mx);
}
void down(int k){
    tr[k<<1].laz=min(tr[k<<1].laz,tr[k].laz);
    tr[k<<1|1].laz=min(tr[k<<1|1].laz,tr[k].laz);
    tr[k<<1].mn=min(tr[k].laz,tr[k<<1].mn);
    tr[k<<1|1].mn=min(tr[k].laz,tr[k<<1|1].mn);
    tr[k].laz=inf;
}
int query(int k,int opl,int opr){
	int l=tr[k].l,r=tr[k].r;
    if(opl<=l&&r<=opr){
        return tr[k].mx;
    }
    if(tr[k].laz!=inf) down(k);
    int mid=(l+r)>>1,res=0;
    if(opl<=mid) res=max(res,query(k<<1,opl,opr));
    if(opr>mid) res=max(res,query(k<<1|1,opl,opr));
	return res;
}
void update(int k,int opl,int opr,int val){
	int l=tr[k].l,r=tr[k].r;
	if(opl<=l&&r<=opr){
		tr[k].mn=min(tr[k].mn,val);
		tr[k].laz=min(tr[k].laz,val);
		return ;
	}
	if(tr[k].laz!=inf) down(k);
	int mid=(l+r)>>1;
	if(opl<=mid) update(k<<1,opl,opr,val);
    if(opr>mid) update(k<<1|1,opl,opr,val);
	tr[k].mn=min(tr[k<<1].mn,tr[k<<1|1].mn);
}
int query_path(int x,int y){
	int res=0;
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]]) swap(x,y);
        res=max(res,query(1,id[top[x]],id[x]));
        x=fa[top[x]];
    }
    if(deep[x]>deep[y]) swap(x,y);
    res=max(res,query(1,id[x]+1,id[y]));
    return res;
}
void update_path(int x,int y,int val){
	while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]]) swap(x,y);
        update(1,id[top[x]],id[x],val);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y]) swap(x,y);
    update(1,id[x]+1,id[y],val);
}
void ask_ans(int k){
	if(tr[k].l==tr[k].r){
        ans[rcd[rk[tr[k].l]]]=tr[k].mn-1;
        if(ans[rcd[rk[tr[k].l]]]==inf-1){
            ans[rcd[rk[tr[k].l]]]=-1;
        }
        return ;
    }
	if(tr[k].laz!=inf) down(k);
	ask_ans(k<<1),ask_ans(k<<1|1);
}
signed main(){
	//freopen("test.in","r",stdin);
	//freopen("vio.out","w",stdout);
	scanf("%d%d%d",&n,&m,&xkl);
	for(int i=1,u,v,w;i<=m;++i){
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w,i);
		//add(v,u,w,i);
	}
	kruskal();
	memset(fa,0,sizeof(fa));
	deep[1]=1;
	dfs(1,0);DFS(1,1);
	build(1,1,n);
	for(int i=1;i<=cnt;++i){
		if(ed[i].flag==0){
			//cout<<ed[i].id<<endl;
			ans[ed[i].id]=query_path(ed[i].fr,ed[i].to)-1;
			//cout<<ans[ed[i].id]<<endl;
			//cout<<ed[i].fr<<' '<<ed[i].to<<endl;
			update_path(ed[i].fr,ed[i].to,ed[i].val);
		}
	}
	ask_ans(1);
	for(int i=1;i<=m;++i)
		printf("%d ",ans[i]);
	puts("");
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/Juve/p/11576165.html