csp-s模拟45,50 weight,蔬菜,联盟
Posted juve
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\\fracl_12\\rceil+\\lceil\\fracl_22\\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;
以上是关于csp-s模拟45,50 weight,蔬菜,联盟的主要内容,如果未能解决你的问题,请参考以下文章