题目
T1 仙人图(II)
Description
如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus)。所谓简单回路就是指在图上不重复经过任何一个顶点的回路。
举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:(4,3,2,1,6,5,4)、(7,8,9,10,2,3,7)以及(4,3,7,8,9,10,2,1,6,5,4),而(2,3)同时出现在前两个的简单回路里。另外,第三张图也不是仙人图,因为它并不是连通图。
显然,仙人图上的每条边,或者是这张仙人图的桥(bridge),或者在且仅在一个简单回路里,两者必居其一。定义在图上两点之间的距离为这两点之间最短路径的距离。定义一个图的直径为这张图相距最远的两个点的距离。现在我们假定仙人图的每条边的权值都是1,你的任务是求出给定的仙人图的直径。
Input
输入的第一行包括两个整数n和m(1≤n≤50,000以及0≤m≤10,000)。其中n代表顶点个数,我们约定图中的顶点将从1到n编号。
接下来一共有m行。代表m条路径。每行的开始有一个整数k(2≤k≤1000),代表在这条路径上的顶点个数。接下来是k个1到n之间的整数,分别对应了一个顶点,相邻的顶点表示存在一条连接这两个顶点的边。一条路径上可能通过一个顶点好几次,比如对于第一个样例,第一条路径从3经过8,又从8返回到了3,但是我们保证所有的边都会出现在某条路径上,而且不会重复出现在两条路径上,或者在一条路径上出现两次。
Output
只需输出一个数,这个数表示仙人图的直径长度。
Sample Input
15 3
9 1 2 3 4 5 6 7 8 3
7 2 9 10 11 12 13 10
5 2 14 9 15 10
Sample Output
8
Hint
样例解释:
T2 Prime的把妹计划
Description
Input
Output
Sample Input
Sample Output
Data Constraint
T3 Segment
Description
要求在平面直角坐标系下维护两个操作:
- 在平面上加入一条线段。记第 i条被插入的线段标号为 i。
- 给定一个数 k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号。
Input
Output
对于每个 0操作,输出一行,包含一个正整数,表示交点最靠上的线段的编号。若不存在与直线相交的段,答案为 0。
Sample Input
6
1 8 5 10 8
1 6 7 2 6
0 2
0 9
1 4 7 6 7
0 5
Sample Output
2
0
3
Data Constraint
对于 30% 的数据, n ≤ 1000
对于 100% 的数据, 1 ≤ n ≤ 10^5, 1 ≤ k, x0, x1 ≤ 39989, 1 ≤ y0 ≤ y1 ≤ 10^9。
总结
高中阶段的第一场比赛
早上要做大扫除,晚到一个小时感觉真不好……
T1
看到仙人掌,一眼圆方树。
做成一棵树之后DP即可,还是老套路,设
f
i
f_i
fi表示以 i 为根的子树中以 i 为一端的路径的最大长度,
g
i
g_i
gi表示以 i 为根的子树的直径长度。
然而发现方点要特殊处理。
在树上处理起来太麻烦了,于是把环拉成一个序列,设
h
i
h_i
hi表示在环上顺时针数第 i-1 个点的 f 值(不考虑第一个点,即成为方点的父亲的那个点)。
然后就可以写出状态转移方程了(G是当前要更新的那个点的g,siz表示环的大小)
G
=
max
(
h
i
+
h
j
+
min
(
j
−
i
,
s
i
z
−
j
+
i
)
)
,
1
<
i
<
j
≤
s
i
z
G=\max(h_i+h_j+\min(j-i,siz-j+i)),1<i<j\leq siz
G=max(hi+hj+min(j−i,siz−j+i)),1<i<j≤siz
但是这样子优化不了DP,那个min实在是太难受了。
于是把原来的序列再重复一遍(注意留一个空的位置给第一个点),这样子状态转移方程就变成这个样子了:
G
=
max
(
h
i
+
h
j
+
j
−
i
)
,
1
<
i
<
j
≤
s
i
z
,
j
−
i
≤
⌊
s
i
z
2
⌋
G=\max(h_i+h_j+j-i),1<i<j\leq siz,j-i\leq\left \lfloor\frac{siz}{2}\right \rfloor
G=max(hi+hj+j−i),1<i<j≤siz,j−i≤⌊2siz⌋
单调队列优化即可。
想不到我第一次打圆方树(还是在考场上打)居然能切题!
T2
考场上打了个
O
(
n
2
)
O(n^2)
O(n2)暴力,居然WA 5了,心态大崩。
以下是SLS大佬的分块做法:
发现n只有50000,而且时间限制是4s,可以分块。
先考虑
∀
l
≤
i
≤
r
,
a
l
≥
a
i
≥
a
r
\forall l\leq i\leq r,a_l\geq a_i\geq a_r
∀l≤i≤r,al≥ai≥ar的情况。
定义
l
i
l_i
li表示以 i 为“艄”序列的右端点,左端点最小是多少,
r
i
r_i
ri表示以 i 为“艄”序列的左端点,右端点最大是多少。这两个东西都可以用单调栈快速地求出来。
接着把
[
1
,
n
]
[1,n]
[1,n]分成
n
\sqrt{n}
n块,设
f
i
,
j
f_{i,j}
fi,j表示第 i 块的左端点到第 j 块的右端点中最长的“艄”序列的长度。那么
f
i
,
j
=
{
f
i
,
j
−
1
,
min
l
j
j
i
,
r
i
≥
j
f_{i,j}= \begin{cases} f_{i,j-1},\\ \min_{l_j}^j i,r_i\geq j \end{cases}
fi,j={fi,j−1,minljji,ri≥j
min那里可以用线段树求,最后倒过来再搞一次就好了。
时间复杂度
O
(
n
n
log
2
n
)
O(n\sqrt n \log_2n)
O(nnlog2n)
虽然码量较大,但是没什么细节。
T3
李超树板题。
考场上想怎么把直线扔到线段树上处理,结果发现好像要维护一个长得很奇怪的图形。
其实是我对线段树理解不够深,利用线段树递归logN层,把大小为N的区间划分为大小为1的区间的特点可以大搞事情。
每个点上都维护一个标记表示当前区间里的所有线段中,k*mid+b最大的那条线段。
如果被替换掉的线段(或者是替换失败的线段)在当前区间中某段比替换成功的线段要优,可以继续递归下去;否则就没有递归下去的意义了。
最后单点查询时一路更新答案即可。
总结
暴力正确率不够,不是模拟的分值较高的暴力一定要对拍。
CODE
T1
#include<cstdio>
using namespace std;
#define M 200005
#define N 100005
struct graph
{
int fir[N],to[M],nex[M],s;
inline void inc(int x,int y)
{
to[++s]=y,nex[s]=fir[x],fir[x]=s;
to[++s]=x,nex[s]=fir[y],fir[y]=s;
}
}a;
int que[N],f[N],g[N],h[N],fir[N],to[N],nex[N],len[N],dfn[N],low[N],dis[N],sta[N],ord[N],n,s,top,cnt,node;
inline char gc()
{
static char buf[100005],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100005,stdin),l==r)?EOF:*l++;
}
inline void read(int &k)
{
char ch;
while(ch=gc(),ch<'0'||ch>'9');k=ch-'0';
while(ch=gc(),ch>='0'&&ch<='9') k=k*10+ch-'0';
}
inline void inc(int x,int y,int z)
{to[++s]=y,len[s]=z,nex[s]=fir[x],fir[x]=s;}
inline int mymin(int x,int y){return x<y?x:y;}
inline int mymax(int x,int y){return x>y?x:y;}
void tarjan(int u)
{
int x,v,i,sum,num;
dfn[u]=low[u]=++cnt,sta[++top]=u;
for(i=a.fir[u];i;i=a.nex[i])
{
v=a.to[i];
if(!dfn[v])
{
dis[v]=dis[u]+1;
tarjan(v);
if(low[u]>low[v]) low[u]=low[v];
if(low[v]==dfn[u])
{
inc(u,++node,0);
sum=dis[sta[top]]-dis[u]+1;
for(x=0;x!=v;--top)
{
x=sta[top];
ord[x]=dis[x]-dis[u];
inc(node,x,mymin(ord[x],sum-ord[x]));
}
}
}
else if(low[u]>dfn[v]) low[u]=dfn[v];
}
}
void dfs(int u)
{
int i,v;
for(i=fir[u];i;i=nex[i])
{
v=to[i],dfs(v);
if(g[v]>g[u]) g[u]=g[v];
if(f[v]+len[i]>f[u]) f[u]=f[v]+len[i];
}
if(u>n)
{
int siz=0,head=1,tail=1,max;
for(i=fir[u];i;i=nex[i])
{
v=to[i],h[ord[v]]=f[v];
if(ord[v]>siz) siz=ord[v];
}
++siz,max=siz>>1;
que[1]=1,h[siz+1]=h[1];
for(i=2;i<siz;++i)
{
h[siz+i]=h[i];
while(i-que[head]>max) ++head;
g[u]=mymax(g[u],h[i]+h[que[head]]+i-que[head]);
while(head<=tail&&h[que[tail]]-que[tail]<=h[i]-i) --tail;
que[++tail]=i;
}
for(i=siz+1;i<siz+max;++i)
{
while(i-que[head]>max) ++head;
g[u]=mymax(g[u],h[i]+h[que[head]]+i-que[head]);
}
}
else
{
int max1=0,max2=0;
for(i=fir[u];i;i=nex[i])
{
v=to[i];
if(f[v]>max1) max2=max1,max1=f[v];
else if(f[v]>max2) max2=f[v];
}
if(g[u]<max1+max2) g[u]=max1+max2;
}
if(g[u]<f[u]) g[u]=f[u];
}
int main()
{
int m,k,x,y;
read(n),read(m),node=n;
for(int i=1;i<=m;++i)
{
read(k),read(y);
for(int j=2;j<=k;++j) read(x),a.inc(x,y),y=x;
}
tarjan(1),dfs(1);
printf("%d\n",g[1]);
return 0;
}
T2
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define rson k<<1|1
#define lson k<<1
#define N 50005
struct query{int x,y,ans;}qry[N];
int a[N],left[N],right[N],sta[N],max[250005],min[250005],f[235][235],fir[235],n,q,siz,tot;
inline char gc()
{
static char buf[50005],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,50005,stdin),l==r)?EOF:*l++;
}
inline void read(int &k)
{
char ch;
while(ch=gc(),ch<'0'||ch>'9');k=ch-'0';
while(ch=gc(),ch>='0'&&ch<='9') k=k*10+ch-'0';
}
inline int mymax(int x,int y){return x>y?x:y;}
inline int mymin(int x,int y){return x<y?x:y;}
void build(int k,int l,int r)
{
if(l==r) max[k]=right[l],min[k]=left[l];
else
{
int mid=l+r>>1;
build(lson,l,mid);
build(rson,mid+1,r);
max[k]=mymax(max[lson],max[rson]);
min[k]=mymin(min[lson],min[rson]);
}
}
int qrymax(int k,int l,int r,int x,int y,int z)
{
if(max[k]<=z) return 0;
if(l==r) return l;
int mid=l+r>>1,tmp;
if(y<=mid) return qrymax(lson,l,mid,x,y,z);
if(x>mid) return qrymax(rson,mid+1,r,x,y,z);
tmp=qrymax(lson,l,mid,x,mid,z);
return tmp?tmp:qrymax(rson,mid+1,r,mid+1,y,z);
}
int qrymin(int k,int l,int r,int x,int y,int z)
{
if(min[k]>z) return 0;
if(l==r) return l;
int mid=l+r>>1,tmp;
if(y<=mid) return qrymin(lson,l,mid,x,y,z);
if(x>mid) return qrymin(rson,mid+1,r,x,y,z);
tmp=qrymin(rson,mid+1,r,mid+1,y,z);
return tmp?tmp:qrymin(lson,l,mid,x,mid,z);
}
inline void calc()
{
build(1,1,n);
memset(f,0,sizeof(f));
for(int i=1;i<=tot;++i)
for(int j=fir[i];j<fir[i+1];++j)
f[i][i]=mymax(f[i][i],j-qrymax(1,1,n,mymax(left[j]+1,fir[i]),j,j)+1);
for(int len=1;len<tot;++len)
for(int i=1,j=len+1;j<=tot;++i,++j)
{
f[i][j]=f[i][j-1];
for(int k=fir[j];k<fir[j+1];++k)
f[i][j]=mymax(f[i][j],k-qrymax(1,1,n,mymax(left[k]+1,fir[i]),k,k)+1);
}
for(int i=1,ans,l,r;i<=q;++i)
{
ans=0;
l=(qry[i].x-1)/siz+1;
r=(qry[i].y-1)/siz+1;
if(qry[i].y<fir[r+1]-1)
for(int j=fir[r--];j<=qry[i].y;++j) ans=mymax(ans,j-qrymax(1,1,n,mymax(left[j]+1,qry[i].x),j,j)+1);
if(qry[i].x>fir[l])
{
++l;
if(f[l][r]>ans) ans=f[l][r];
for(int j=qry[i].x;j<fir[l];++j) ans=mymax(ans,qrymin(1,1,n,j,mymin(right[j]-1,qry[i].y),j)-j+1);
}
else if(f[l][r]>ans) ans=f[l][r];
if(ans>qry[i].ans) qry[i].ans=ans;
}
}
int main()
{
int i,j,x,y,top;
read(n),siz=sqrt(n);
tot=n/siz;if(tot*siz<n) ++tot;
for(i=1;i<=tot;++i) fir[i]=(i-1)*siz+1;
fir[tot+1]=n+1;
for(i=1;i<=n;++i) read(a[i]);
read(q);
for(i=1;i<=q;++i) read(qry[i].x),read(qry[i].y);
for(i=1,top=0;i<=n;++i)
{
while(top&&a[i]<=a[sta[top]]) --top;
left[i]=sta[top],sta[++top]=i;
}
for(i=n,top=0;i;--i)
{
while(top&&a[i]>=a[sta[top]]) --top;
right[i]=top?sta[top]:n+1,sta[++top]=i;
}
calc();
for(i=1,top=0;i<=n;++i)
{
while(top&&a[i]>=a[sta[top]]) --top;
left[i]=sta[top],sta[++top]=i;
}
for(i=n,top=0;i;--i)
{
while(top&&a[i]<=a[sta[top]]) --top;
right[i]=top?sta[top]:n+1,sta[++top]=i;
}
calc();
for(i=1;i<=q;++i) printf("%d\n",qry[i].ans);
return 0;
}
T3
#include<cstdio>
using namespace std;
#define lson k<<1
#define rson k<<1|1
#define P2 1000000000
#define P1 39989
#define N 100005
inline char gc()
{
static char buf[100005],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100005,stdin),l==r)?EOF:*l++;
}
inline void read(int &k)
{
char ch;
while(ch=gc(),ch<'0'||ch>'9');k=ch-'0';
while(ch=gc(),ch>='0'&&ch<='9') k=k*10+ch-'0';
}
int tag[500005],ans;double top;
struct line{int l,r;double k,b;}a[N];
inline void swap(int &x,int &y){int z=x;x=y,y=z;}
inline double calc(int k,int x){return x*a[k].k+a[k].b;}
void modify(int k,int l,int r,int x)
{
if(a[x].l<=l&&r<=a[x].r)
{
if(!tag[k]) tag[k]=x;
else
{
bool b1=calc(tag[k],l)<calc(x,l),b2=calc(tag[k],r)<calc(x,r);
if(b1&&b2) tag[k]=x;
else if(b1||b2)
{
int mid=l+r>>1;
if(calc(tag[k],mid)<calc(x,mid)) swap(tag[k],x);
if(calc(tag[k],l)<calc(x,l)) modify(lson,l,mid,x);
else modify(rson,mid+1,r,x);
}
}
}
else
{
int mid=l+r>>1;
if(a[x].l<=mid) modify(lson,l,mid,x);
if(a[x].r>mid) modify(rson,mid+1,r,x);
}
}
void qry(int k,int l,int r,int x)
{
if(tag[k]&&(top<calc(tag[k],x)||top==calc(tag[k],x)&&tag[k]<ans)) top=calc(tag[k],x),ans=tag[k];
if(l==r) return;
int mid=l+r>>1;
if(x<=mid) qry(lson,l,mid,x);
else qry(rson,mid+1,r,x);
}
int main()
{
int n,i,j,cnt=0,opt,x0,y0,x1,y1,x;
double k;
read(n);
for(i=1;i<=n;++i)
{
read(opt);
if(opt==0)
{
read(x),x=(x+ans-1)%P1+1;
top=0,ans=0,qry(1,1,P1,x);
printf("%d\n",ans);
}
else
{
read(x0),read(y0),read(x1),read(y1);
x0=(x0+ans-1)%P1+1,y0=(y0+ans-1)%P2+1;
x1=(x1+ans-1)%P1+1,y1=(y1+ans-1)%P2+1;
if(x0>x1) swap(x0,x1),swap(y0,y1);
if(x0==x1) a[++cnt]=(line){x0,x0,0,double(y0>y1?y0:y1)};
else
{
k=(y1-y0)/(double)(x1-x0);
a[++cnt]=(line){x0,x1,k,y0-x0*k};
}
modify(1,1,P1,cnt);
}
}
return 0;
}