标签:put uil 水过 ems http efi nod csp try
题面: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;
}
标签:put uil 水过 ems http efi nod csp try
原文地址:https://www.cnblogs.com/Juve/p/11576165.html