【日常摸鱼】牛客挑战赛2
前言
略了。。不多bb
A Cut
等同于合并果子,水题略了
B Travel
裸最短路,水题略了
C Butterfly
链接
https://ac.nowcoder.com/acm/contest/17/C
题意
给定一个n*m的矩阵,矩阵元素由X和O构成,请求出其中最大的由X构成的蝴蝶形状。
由X构成的蝴蝶形状的定义如下:
存在一个中心点,并且其往左上、左下、右上、右下四个方向扩展相同的长度(扩展的长度上都是X),且左上顶点与左下顶点、右上顶点与右下顶点之间的格子全由X填充。我们不在意在蝴蝶形状内部是X还是O。
例如:
XOOOX
XXOXX
XOXOX
XXOXX
XOOOX
是一个X构成的蝴蝶形状。
X也是。
而
XOOX
OXXO
OXXO
XOXX
不是(不存在中心点)。
(n,mleq 2000)
题解
因为蝴蝶形中间可以有空的,不好二分。。我们先想办法转化成能二分的东西。。
一个蝴蝶形可以看做两个(一竖+一斜杠),我们先算出从一个点(i,j)最大的能向左或向右的(竖+斜)。
这个是连续的,因为能有长度为x的(竖+斜),就能有长度为x-1的。
然后一个蝴蝶形就是两个同一行的(竖+斜)的组合。
设R(i,j)和L(i,j)分别表示(i,j)可以向左或向右延伸的最大的(竖+斜)。
然后假设我们能用(i,j)和(i,k)组合出一个蝴蝶形。
于是有:
(R(i,j)geq k-j+1)
(L(i,k)geq k-j+1)
移项得:
(R(i,j)+j-1 geq k geq j)
(j geq k+1-L(i,k))
对于同一行,按照这两个不等式,就能套用各种数据结构了。。我用的是set。。
(Code)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL INF=1e18;
const int N=2e5+100;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
void get_min(LL &x,LL y){
if(x>y) x=y;
}
int n,m;
char s[2010];
int mp[2010][2010];
int d[2010][2010],dr[2010][2010],dl[2010][2010];
int L[2010][2010],R[2010][2010];
set<int> S[2];
set<int>::iterator it;
int id[2010],val[2010];
bool cmp(int x,int y){
return val[x]<val[y];
}
int main(){
int x,ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%s",s+1);
for(int j=1;j<=m;++j){
if(s[j]=='X') mp[i][j]=1;
else mp[i][j]=0;
}
}
for(int i=n;i>=1;--i){
for(int j=1;j<=m;++j){
if(!mp[i][j]) d[i][j]=dr[i][j]=dl[i][j]=0;
else{
d[i][j]=dr[i][j]=dl[i][j]=1;
if(i<n) d[i][j]+=d[i+1][j];
if(i<n&&j<m) dr[i][j]+=dr[i+1][j+1];
if(i<n&&j>1) dl[i][j]+=dl[i+1][j-1];
}
}
}
for(int i=n;i>=1;--i){
for(int j=1;j<=m;++j){
L[i][j]=min(d[i][j],dr[i][j]);
R[i][j]=min(d[i][j],dl[i][j]);
}
}
for(int i=1;i<=m;++i) id[i]=i;
for(int i=1;i<=n;++i){
S[0].clear();S[1].clear();
int k;
for(k=1;k<=m;++k){
val[k]=k+1-R[i][k];
}
sort(id+1,id+1+m,cmp);
S[0].insert(0);S[1].insert(0);k=1;
for(int j=1;j<=m;++j){
while(k<=m&&val[id[k]]<=j){
S[(id[k]&1)].insert(id[k]);
++k;
}
it=S[(j&1)].upper_bound(L[i][j]+j-1);
--it;
x=*it;
if(x>=j&&x<=L[i][j]+j-1){
ans=max(ans,x-j+1);
}
}
}
printf("%d
",ans);
return 0;
}
D Delete
链接
https://ac.nowcoder.com/acm/contest/17/D
题意
给定一张n个点,m条边的带权有向无环图,同时给定起点S和终点T,一共有q个询问,每次询问删掉某个点和所有与它相连的边之后S到T的最短路,询问之间互相独立(即删除操作在询问结束之后会立即撤销),如果删了那个点后不存在S到T的最短路,则输出-1。
(n,qleq 100000,mleq 200000)
题解
看到是DAG上求距离,先拓扑排序一波。
排好之后,ST间的每条路径都相当于在拓扑序上按顺序选几个点连起来。
我们先预处理S到所有点的距离和所有点到T的距离。
如果删的是拓扑序在S前或在T后的,直接输出ST的最短距离。
对于其他点,考虑一条路径对答案的影响,相当于对这条路径没经过的所有点取个min。
这样的话我们枚举每一条边(x,y),对拓扑序在xy之间的所有点用dis(S,x)+len(x,y)+dis(y,T)来取min。
这样的话问题就是区间min和单点查询了,可以用线段树。
(Code)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL INF=1e18;
const int N=2e5+100;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,m,S,T;
struct edge{int x,y;LL z;}e[N];
vector<int> L[N],R[N];
int du[N];
int q[N];
int num[N];
LL ds[N],dt[N];
LL mn[N<<1];
void upd(int l,int r,int id,LL w,int lq,int rq){
if(lq==l&&rq==r){
mn[id]=min(mn[id],w);
return;
}
int mid=(l+r)>>1;
if(lq>mid) upd(mid+1,r,id<<1|1,w,lq,rq);
else if(rq<=mid) upd(l,mid,id<<1,w,lq,rq);
else{
upd(l,mid,id<<1,w,lq,mid);
upd(mid+1,r,id<<1|1,w,mid+1,rq);
}
return;
}
LL ask(int l,int r,int id,int x){
if(l==r) return mn[id];
int mid=(l+r)>>1;
if(x<=mid) return min(ask(l,mid,id<<1,x),mn[id]);
else return min(ask(mid+1,r,id<<1|1,x),mn[id]);
}
int main(){
n=read();m=read();S=read();T=read();
for(int i=1;i<=m;++i){
e[i].x=read();e[i].y=read();e[i].z=read();
++du[e[i].y];
R[e[i].x].push_back(i);
L[e[i].y].push_back(i);
}
int l=1,r=0,x,y;edge ed;LL ans;
for(int i=1;i<=n;++i) if(!du[i]) q[++r]=i;
while(l<=r){
x=q[l++];
for(int i=0;i<R[x].size();++i){
y=e[R[x][i]].y;
--du[y];
if(!du[y]) q[++r]=y;
}
}
for(int i=1;i<=n;++i){
num[q[i]]=i;
}
for(int i=1;i<=n;++i){
ds[i]=dt[i]=INF;
}
ds[S]=0;
for(int i=1;i<=r;++i){
for(int j=0;j<R[q[i]].size();++j){
ed=e[R[q[i]][j]];
ds[ed.y]=min(ds[ed.y],ds[ed.x]+ed.z);
}
}
dt[T]=0;
for(int i=r;i>=1;--i){
for(int j=0;j<L[q[i]].size();++j){
ed=e[L[q[i]][j]];
dt[ed.x]=min(dt[ed.x],dt[ed.y]+ed.z);
}
}
for(int i=1;i<(N<<1);++i) mn[i]=INF;
for(int i=1;i<=m;++i){
ans=ds[e[i].x]+e[i].z+dt[e[i].y];
if(ans<INF&&num[e[i].x]+1<=num[e[i].y]-1)
upd(1,n,1,ans,num[e[i].x]+1,num[e[i].y]-1);
}
int Q=read();
while(Q--){
x=read();
if(num[x]<num[S]||num[x]>num[T]){
if(ds[T]==INF)puts("-1");
else printf("%lld
",ds[T]);
continue;
}
ans=ask(1,n,1,num[x]);
if(ans==INF) puts("-1");
else printf("%lld
",ans);
}
return 0;
}
E Mod
这个窝不会。。。
题解大概是类似辗转相减的方法。。
细节很多很不好写。。我就放弃了