前言

  • 一套码农题……

T1

Description

  • 给定一棵\(n(\in[2,10^5])\)个点的树,\(m(≤10^5)\)次询问,每次询问有两个不相同的点,要让所有点走到这两个点之一(走一条边耗费1单位时间,所有点同时出发),求最少耗时。

SolutionⅠ

  • 这题有一个简单又自然的方法:LCT!
  • 我们用LCT求出询问点的中间两点,断开其中的边;然后分别把两个询问点makeroot,查询各自的树的深度最大值。
  • 注意虚边的所带出的深度最大值也要算上;而维护这东西还要打个set/multiset。而且由于LCT有翻转操作,我们要求一个正着的深度最大值和反着的最大值,翻转的时候直接交换它们即可。

SolutionⅡ

  • 这题是不是有那么一点像……noip2018D3T3?
  • 没错!我们可以倍增!设两个倍增数组\(up[i][j]\)、\(dw[i][j]\)分别表示\(i\)到它的第\(2^j\)个祖先的链上所有点的儿子的子树(不把\(i\)的子树计算在内,后文对于这种类似的简记为\(a\)到\(b\))走到它的\(2^j\)个祖先/走到\(i\)的答案。
  • 对于一个询问\((x,y)\)(不妨钦定\(deep_x≥deep_y\)),我们找到\(z=lca(x,y)\),并找到\((x,y)\)的中间点\(z1\)。那么答案的计算就分为六个部分:1.\(x\)的子树全部走到\(x\);2.\(x\)到\(z1\)全部走到\(x\);3.\(z1\)到\(z\)全部走到\(y\);4.\(y\)到\(z\)全部走到\(y\);5.\(y\)的子树全部走到\(y\);6.\(z\)的子树以外所有点以及\(z\)的儿子(不含\(x\)、\(y\)的祖先)的子树全部走到\(y\)。
Code
  • 这倍增,比LCT还难打……
#include <cstdio>
#include <vector>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll; const int N=11e4;
int n,x,y,z,f[N][17],dep[N],in[N],out[N],dw[N][17],up[N][17],m,todep,z1,z2,ans;
vector<int> e[N]; void MAX(int&x,int y) {if(x<y) x=y;}
int max(int x,int y) {return x>y?x:y;} void dfs(int x)
{
vector<int>::iterator it; int y;
for(it=e[x].begin(); it!=e[x].end(); it++)
if((y=*it)^f[x][0])
{
f[y][0]=x, dep[y]=dep[x]+1, dfs(y);
MAX(dw[y][0],in[x]+1);
MAX(up[y][0],in[x]);
MAX(in[x],in[y]+1);
}
int g=0;
for(it--; 233; it--)
{
if((y=*it)^f[x][0])
{
MAX(dw[y][0],g+1);
MAX(up[y][0],g);
MAX(g,in[y]+1);
}
if(it==e[x].begin()) break;
}
} int lca(int x,int y)
{
fd(i,16,0) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
fd(i,16,0) if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
} int main()
{
scanf("%d",&n);
fo(i,1,n-1)
{
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
dfs(dep[1]=1);
fo(i,1,16)
fo(x,1,n)
{
if(!(f[x][i]=f[y=f[x][i-1]][i-1])) continue;
dw[x][i]=max(dw[x][i-1],dw[y][i-1]+(1<<i-1));
up[x][i]=max(up[x][i-1]+(1<<i-1),up[y][i-1]);
}
fo(x,1,n)
{
z1=x;
fd(i,16,0)
if(f[z1][i])
{
MAX(out[x],dw[z1][i]+dep[x]-dep[z1]);
z1=f[z1][i];
}
}
for(scanf("%d",&m); m--;)
{
int x,y;
scanf("%d%d",&x,&y);
if(dep[x]<dep[y]) swap(x,y);
z=lca(x,y);
todep=(z^y?dep[x]-(dep[x]+dep[y]-2*dep[z]-1>>1):(dep[x]+dep[y]>>1)+1);
z1=x, ans=max(in[x],out[z]+dep[y]-dep[z]);
fd(i,16,0)
if(dep[f[z1][i]]>=todep)
{
MAX(ans,dw[z1][i]+dep[x]-dep[z1]);
z1=f[z1][i];
}
fd(i,16,0)
if(dep[f[z1][i]]>dep[z])
{
MAX(ans,up[z1][i]+dep[f[z1][i]]+dep[y]-2*dep[z]);
z1=f[z1][i];
}
x=z1;
if(z^y)
{
MAX(ans,in[z1=y]);
fd(i,16,0)
if(dep[f[z1][i]]>dep[z])
{
MAX(ans,dw[z1][i]+dep[y]-dep[z1]);
z1=f[z1][i];
}
for(vector<int>::iterator it=e[z].begin(); it!=e[z].end(); it++)
if((z2=*it)^f[z][0]&&z2^x&&z2^z1)
MAX(ans,in[z2]+1+dep[y]-dep[z]);
}
else MAX(ans,up[z1][0]);
printf("%d\n",ans);
}
}

T2

Description

  • 给出一棵以1号点为根的\(n(\in[2,10^5])\)个点的树,并会给出每个点的儿子排列(要按照它的顺序求dfn序)。有\(m\)个操作,操作有三种:
  • 操作Ⅰ:询问两个点的距离;
  • 操作Ⅱ:给出v和h,断开v和他父亲的边,然后将它和它第h个祖先相连;
  • 操作Ⅲ:求深度为\(k\)的点中dfn序最大的点。

SolutionⅠ

  • 此法由cc dalao提供。
  • 用LCT维护树的形态以及操作Ⅰ;再开一棵splay维护原树的dfn序。由于我们可以用LCT维护子树大小,所以很方便知道要搬多少点,操作Ⅱ迎刃而解;操作Ⅲ的话,可以在splay上二分,尽量走右儿子即可。

SolutionⅡ

  • ETT(欧拉游览树,用splay维护括号序)。
  • 我们把原树的每个点拆成两个括号,左括号值为1,右括号值为-1;这样有一个很好的性质:原树中一个点的深度就等于它所对应的左括号的前缀和。这样的话,我们记录下原树中每个点对应的括号标号;而splay中每个点记录它的值、子树和、子树中最大/最小前缀和。
  • 操作Ⅰ的话,可以找到那两个点所对应的左括号,那它们的lca的深度肯定是两个左括号之间最小的前缀和。
  • 操作Ⅱ就直接把v的子树拎出来,查询第h个祖先就相当于查询dfn序在v之前、深度(前缀和)恰好为\(deep_v-h\)的最后一个点。这个可以在splay上二分,也是尽量走右儿子。
  • 操作Ⅲ就二分出全树中深度为\(k\)的最后一个点。
Code
  • 下面的代码中,在splay上二分后我并没有做伸展操作。这样做并非均摊\(O((n+m)\log_2n)\),而是最坏\(O(nm)\)。不过考虑到出题人懒,数据大部分随机,二分后伸展反而跑得更慢。
#include <cstdio>
#define A son[x][0]
#define B son[x][1]
#define A1 son[y][0]
#define B1 son[y][1]
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std; const int N=21e4;
int n,m,l,a[N],pa[N],to[N],ne[N],la[N],ti,p[N],v[N],fa[N],son[N][2],s[N],mx[N],mn[N],rt; void ins(int x,int y) {static int tot=0; pa[to[++tot]=y]=x, ne[tot]=la[x], la[x]=tot;} void dfs(int x)
{
v[p[++ti]=(x<<1)-1]=1;
for(int i=la[x],y; y=to[i]; i=ne[i]) dfs(y);
v[p[++ti]=x<<1]=-1;
} int max(const int&x,const int&y) {return x>y?x:y;}
int min(const int&x,const int&y) {return x<y?x:y;}
bool so(int x) {return son[fa[x]][1]==x;}
void link(int y,int x,bool k)
{
if(y) son[y][k]=x;
if(x) fa[x]=y;
}
void up(int x)
{
s[x]=s[A]+v[x]+s[B];
mx[x]=max(mx[A],s[A]+v[x]+max(mx[B],0));
mn[x]=min(mn[A],s[A]+v[x]+min(mn[B],0));
}
int build(int l,int r)
{
int mid=l+r>>1,&x=p[mid];
if(l<mid) link(x,build(l,mid-1),0);
if(mid<r) link(x,build(mid+1,r),1);
up(x);
return x;
}
void rot(int x)
{
if(!x) return;
int y=fa[x],z=fa[y],k=so(x),b=son[x][!k];
link(y,b,k);
link(z,x,so(y));
link(x,y,!k);
up(y), up(x);
}
void splay(int x,int y) {for(int f=fa[x]; f^y; rot(x),f=fa[x]) rot(fa[f]^y?so(x)==so(f)?f:x:0);}
int MIN(int&x,const int&y) {if(x>y) x=y;}
int dis(int x,int y)
{
x=(x<<1)-1, y=(y<<1)-1;
int dx,dy,dl;
splay(x,0), dx=s[A]+v[x];
splay(y,0), dy=s[A1]+v[y];
splay(x,y), rt=y;
dl=min(dx,dy);
MIN(dl, A1==x ? s[A]+v[x]+mn[B] : s[A1]+v[y]+mn[A] );
return dx+dy-2*dl;
}
int find(int x,int k)
{
while(233)
{
int k1=k-s[A]-v[x];
if(mn[B]<=k1&&k1<=mx[B]) {k=k1,x=B; continue;}
if(s[A]+v[x]==k) return x&1?x+1>>1:pa[x>>1];
x=A;
}
}
int pre(int x) {splay(x,0); for(x=A;B;x=B); return x;}
int nxt(int x) {splay(x,0); for(x=B;A;x=A); return x;}
void move(int u,int h)
{
int x=(u<<1)-1,L,R,t;
splay(x,0);
pa[u]=find(A,s[A]+v[x]-h);
L=pre(x), R=nxt(x+1);
splay(L,0), splay(R,L);
t=son[R][0], son[R][0]=0;
up(R), up(L);
L=pre(R=pa[u]<<1);
splay(L,0), splay(R,L);
link(R,t,0);
up(R), up(rt=L);
} int main()
{
scanf("%d%d",&n,&m);
fo(i,1,n)
{
scanf("%d",&l);
fo(j,1,l) scanf("%d",&a[j]), pa[a[j]]=i;
while(l) ins(i,a[l--]);
}
dfs(1);
mn[0]=N, mx[0]=-N;
rt=build(1,ti);
int tp,x,y;
while(m--)
{
scanf("%d%d",&tp,&x);
switch(tp)
{
case 1:scanf("%d",&y);
printf("%d\n",x^y?dis(x,y):0);
break; case 2:scanf("%d",&y);
move(x,y);
break; case 3:printf("%d\n",find(rt,x+1));
break;
}
}
}

T3

Description

  • 描述实在太复杂了,上图。

  • 对于30% 的数据,n,m <= 5,数据组数<=7
  • 对于100% 的数据,2 <= n,m <= 50,数据组数<= 17

Solution

  • DP!
  • 可以预处理只有左上、右上、左下、右下、上下方向的象鼻时最多引用的水量。然后可以分为下图所示的三种情况:

  • 当然这还有一种左右向的,由上图旋转90°可得。为了简便,我是将原图的行、列交换。



  • 可以发现第三张图左右(或者上下)翻转就得到第二张图。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define max(x,y) (x>y?x:y)
#define C(a) memset(a,0,sizeof a);
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std; const int N=55;
int n,a[N][N],u[N][N],l[N][N],d[N][N],r[N][N];
int ul[N][N],ur[N][N],dl[N][N],dr[N][N],ud[N][N],ans;
char s[N][N]; inline void MAX(int&x,const int&y) {if(x<y)x=y;} void init(bool k)
{
fo(i,1,n)
{
fo(j,1,n)
{
u[i][j]=max(u[i-1][j],a[i][j]);
l[i][j]=max(l[i][j-1],a[i][j]);
ul[i][j]=max(ul[i][j-1]+u[i][j],ul[i-1][j]+l[i][j]);
}
fd(j,n,1)
{
r[i][j]=max(r[i][j+1],a[i][j]);
ur[i][j]=max(ur[i][j+1]+u[i][j],ur[i-1][j]+r[i][j]);
}
}
fd(i,n,1)
{
fo(j,1,n)
{
d[i][j]=max(d[i+1][j],a[i][j]);
dl[i][j]=max(dl[i][j-1]+d[i][j],dl[i+1][j]+l[i][j]);
}
fd(j,n,1) dr[i][j]=max(dr[i][j+1]+d[i][j],dr[i+1][j]+r[i][j]);
}
if(k) return;
fo(j,1,n)
{
ud[j][j]=0;
fo(i,0,n) MAX(ud[j][j],u[i][j]+d[i+1][j]);
}
fo(i,1,n-1) fo(j,i+1,n) ud[i][j]=ud[i][j-1]+ud[j][j];
} void calc1()
{
fo(i,0,n)
fo(j,0,n)
fo(k,0,n)
fo(l,k+1,n+1)
MAX(ans,ul[i][k]+ur[j][l]+ud[k+1][l-1]+dl[i+1][k]+dr[j+1][l]);
} void calc2()
{
fo(i,0,n)
fo(j,1,i)
fo(k,0,n)
fo(l,1,k)
MAX(ans,ul[i][l-1]+ur[j-1][l]+dl[i+1][k]+dr[j][k+1]);
} int main()
{
while(~scanf("%d",&n))
{
C(u) C(l) C(d) C(r)
C(ul) C(ur) C(dl) C(dr) C(ud)
fo(i,1,n)
{
scanf("%s",s[i]+1);
fo(j,1,n) a[i][j]=s[i][j]^48;
} ans=0;
init(0), calc1(); fo(i,1,n-1) fo(j,i+1,n) swap(a[i][j],a[j][i]);
init(0), calc1(); calc2(); fo(i,1,n) fo(j,1,n/2) swap(a[i][j],a[i][n-j+1]);
init(1), calc2(); printf("%d\n",ans);
}
}

【纪中集训】2019.08.10【省选组】模拟TJ的更多相关文章

  1. 【纪中集训2019.3.23】Deadline

    题意 描述 一个二分图\((A,B)\),每个点额外有一个颜色0或者1: 匹配时,只能相同颜色的点匹配: 给出\(A\)中的颜色,问如何分配\(B\)种的颜色使得\((A,B)\)的最大匹配最小: 范 ...

  2. 【纪中集训2019.3.12】Mas的仙人掌

    题意: ​ 给出一棵\(n\)个点的树,需要加\(m\)条边,每条边脱落的概率为\(p_{i}\) ,求加入的边在最后形成图中仅在一个简单环上的边数的期望: \(1 \le n \ , m \le 1 ...

  3. 【纪中集训2019.3.27】【集训队互测2018】小A的旅行(白)

    题目 描述 ​ \(0-n-1\)的图,满足\(n\)是\(2\)的整数次幂, $ i \to j $ 有 $ A_{i,j} $ 条路径: ​ 一条路径的愉悦值定义为起点和终点编号的\(and\)值 ...

  4. 【纪中集训2019.3.23】IOer

    题目 描述 你要在\(m\)天内,刷\(n\)道题,每天可以刷的题的数目不限: 第\(i\)天可以刷的题目的种类是\(ui+v\): 两种刷题的方案不同当且仅当某天刷题的数量不同或者依次刷题的种类不同 ...

  5. 【纪中集训2019.3.11】Cubelia

    题目: 描述 给出长度为\(n\)的数组\(a\)和\(q\)个询问\(l,r\). 求区间\([l,r]\)的所有子区间的前缀和的最大值之和: 范围: $n \le 2 \times 10^5 , ...

  6. 【纪中集训2019.3.12】Z的礼物

    题意 已知\(a_{i} = \sum_{j=1}^{i} \{^{i} _{j} \}b_{j}\), 给出\(a_{1} 到 a_{n}\) : 求\(b_{l} 到 b_{r}\)在\(1e9+ ...

  7. 【纪中集训2019.3.13】fft

    题意: 描述 一共有\(n+m\)道题,其中\(n\)道答案是\(A\),\(m\)道答案是\(B\): 你事先知道\(n和m\),问在最优情况下的期望答错次数,对\(998244353\)取模: 范 ...

  8. 「中山纪中集训省选组D1T1」最大收益 贪心

    题目描述 给出\(N\)件单位时间任务,对于第\(i\)件任务,如果要完成该任务,需要占用\([S_i, T_i]\)间的某个时刻,且完成后会有\(V_i\)的收益.求最大收益. 澄清:一个时刻只能做 ...

  9. 纪中集训 Day1

    今天早上起来吃饭,发现纪中伙食真的是太差了!!!什么都不热,早餐的面包还好,然后就迎来了美好的早晨= = 早上做一套题,T1T2果断秒,T3一看就是noi原题,还好看过题解会写,然后就愉快的码+Deb ...

  10. 纪中集训 Day 0?

    好吧昨天的等到今天才来写,现在超不想刷题,来写下blog吧= = 坐了近10H的火车终于来到了中山市 火车上在看空之境界,等有时间补下动画吧= = 到了宿舍各种不习惯(现在才发现还是母校好QAQ)然后 ...

随机推荐

  1. Mac 上使用svn 记录

    .启动svn服务器 svnadmin create /Users/liuwei/Desktop/svn/UI 如果本地有 UI这个目录了就不用再运行 使用这句就可以了 svnserve -d -r / ...

  2. logstash配置文件详解

     logstash pipeline 包含两个必须的元素:input和output,和一个可选元素:filter. 从input读取事件源,(经过filter解析和处理之后),从output输出到目标 ...

  3. AngularJS ng-model 指令

    AngularJS ng-model 指令 ng-model 指令用于绑定应用程序数据到 HTML 控制器(input, select, textarea)的值. 代码示例如下: <!DOCTY ...

  4. windows10 cortana 不能搜索解决办法

    不太确定是某次系统更新或安装VS软件之后, 发现windows10 cortana 搜索的结果是空白了, 搜索了相关帖子, 试遍所有方法都无效, 最后在联网的情况下, 只用了在powershell中重 ...

  5. 利用单臂路由实现VLAN间路由(有1个疑问)

    配置PC机: PC1:IP 192.168.1.1 :掩码:255.255.255.0:网关:192.168.1.254  VLAN 10 PC2:IP 192.168.2.1 :掩码:255.255 ...

  6. static关键字_1

    static关键字   1.在类中,用static声明的成员变量为静态成员变量,它为该类的公用变量,在第一次使用时被初始化,对于该类的所以对象来说,static成员变量只有一份. 2.用static声 ...

  7. 工控PLC中,关于定时器TON,TOF,的一点新认知,或者说醒悟吧!

    PLC  中的定时器,都是放在一个具体PRG任务单元中的,而PRG单元需要放在具体固定的周期循环任务中才能被执行,而这个周期循环任务的循环周期 T: 与定时器的定时时间T0:    T与T0 的数量级 ...

  8. 第6章 RPC之道

    6.1 认识RPC 分布式.微服务的架构思维中都不能缺少 RPC 的影子 RPC(Remote Procedure Call)远程过程调用.通过网络在跨进程的两台服务器之间传输信息,我们使用的时候不用 ...

  9. golang的数据类型之基本数据类型的默认值和转换

    默认值: 整型的默认值:0 浮点型的默认值:0字符串的默认值:""   //空布尔类型的默认值:false package mainimport "fmt" f ...

  10. 绿盟扫出来个http host 漏洞

    这个漏洞搞了大半天,想过从后台拦截,也想过从前台拦截,都是无从下手!网上也找了很多资料,有点乱,后来自己结合网上的办法,搞出如下解决办法:在tomcat server.xml里配置host 因为外网是 ...