WC 2018 题解

一些感受.jpg

题目难度相较前些年会相对简单一点?(FAKE.jpg

平均码量符合WC风格?(甚至更多一点

出题人良心!

[WC2018] 通道

  • 一个不知道对不对的$\log ^3$大常数解法:对于第一棵树边分治,第二颗树建虚树然后边分治,最后一颗树再建虚树DP

显然我是不可能写的,这辈子都不可能写的。

那么显然,我们还是要分析性质的!

我们想到把第一个树的点权第二个树的点权附到第三棵树上,然后做直径就完事了。

对于边权非负,贪心解决树的直径是对的,所有链接两个树之后的直径显然就是原先的$4$个直径的断点的$C(4,2)$的组合最大的那个。

然后显然,第一棵树的点权即为$dis_x-dis_{now}$,第二颗树的点权即为:$dis_x$,这样,附到第三棵树上求直径即可。

然后首先相当于是一开始只有一坨的点,然后在第二颗树的虚树上合并即可,那么合并的过程中就可以知道在第二颗树的$LCA$了,更新答案的时候减去即可。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define ll long long
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
char buf[100000],*p1,*p2;
inline int rd()
{
register int x=0;register char c=nc();
while(c<48)c=nc();while(c>47)x=((x+(x<<2))<<1)+(c^48),c=nc();
return x;
}
inline ll Rd()
{
register ll x=0;register char c=nc();
while(c<48)c=nc();while(c>47)x=((x+(x<<2))<<1)+(c^48),c=nc();
return x;
}
int n,size,q[100005],col[100005],lg[200005];ll val[100005],ans; namespace T3
{
const int N = 100005;
struct node{int to,next;ll val;}e[N<<1];
int head[N],cnt,idx[N],tims;ll mn[18][N<<1],dis[N];
inline void add(int x,int y,ll z){e[cnt]=(node){y,head[x],z};head[x]=cnt++;}
void dfs(int x,int from)
{
mn[0][++tims]=dis[x];idx[x]=tims;
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from)dis[to1]=dis[x]+e[i].val,dfs(to1,x),mn[0][++tims]=dis[x];
}
}
void init()
{
memset(head,-1,sizeof(head));ll z;
for(int i=1,x,y;i<n;i++)x=rd(),y=rd(),z=Rd(),add(x,y,z),add(y,x,z);
dfs(1,0);for(int i=2;i<=tims;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=17;i++)
for(int j=1;j+(1<<i)-1<=tims;j++)
mn[i][j]=min(mn[i-1][j],mn[i-1][j+(1<<i-1)]);
}
inline ll get_lca(int x,int y){if(x==y)return 0ll;if(x>y)swap(x,y);int tmp=lg[y-x+1];return min(mn[tmp][x],mn[tmp][y-(1<<tmp)+1]);}
inline ll get_dis(int x,int y){if(x==y)return 0ll;return dis[x]+dis[y]-(get_lca(idx[x],idx[y])<<1);}
} namespace T2
{
const int N = 100005;
struct node{int to,next;ll val;}e[N<<1];
int head[N],cnt,mn[18][N<<1],idx[N],tims,dep[N];ll dis[N];
inline void add(int x,int y,ll z){e[cnt]=(node){y,head[x],z};head[x]=cnt++;}
inline void add(int x,int y){e[cnt]=(node){y,head[x],0ll};head[x]=cnt++;}
inline int cmp1(int x,int y){return dep[x]<dep[y]?x:y;}
inline bool cmp(int x,int y){return idx[x]<idx[y];}
void dfs(int x,int from)
{
mn[0][++tims]=x;idx[x]=tims;dep[x]=dep[from]+1;
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from)dis[to1]=dis[x]+e[i].val,dfs(to1,x),mn[0][++tims]=x;
}
}
inline int get_lca(int x,int y){if(x==y)return x;x=idx[x],y=idx[y];if(x>y)swap(x,y);int tmp=lg[y-x+1];return cmp1(mn[tmp][x],mn[tmp][y-(1<<tmp)+1]);}
inline ll get_dis(int x,int y){return dis[x]+dis[y]-(dis[get_lca(x,y)]<<1);}
void init()
{
memset(head,-1,sizeof(head));memset(col,-1,sizeof(col));ll z;
for(int i=1,x,y;i<n;i++)x=rd(),y=rd(),z=Rd(),add(x,y,z),add(y,x,z);
dfs(1,0);
for(int i=1;i<=17;i++)
for(int j=1;j+(1<<i)-1<=tims;j++)
mn[i][j]=cmp1(mn[i-1][j],mn[i-1][j+(1<<i-1)]);
memset(head,-1,sizeof(head));cnt=0;tims=0;
}
struct DP
{
int x,y;ll len;
DP(){x=y=0;len=-1ll;}
DP(int a,int b){x=a,y=b,len=T3::get_dis(a,b)+val[a]+val[b]+dis[a]+dis[b];}
DP(int a,int b,ll c){x=a,y=b,len=c;}
inline bool operator < (const DP &a) const {return len<a.len;}
inline friend DP operator + (const DP &a,const DP &b)
{
if(!a.x)return b;if(!b.x)return a;
return max(max(a,b),max(max(DP(a.x,b.x),DP(a.y,b.x)),max(DP(a.x,b.y),DP(a.y,b.y))));
}
inline friend DP operator * (const DP &a,const DP &b)
{
if(!a.x)return DP();if(!b.x)return DP();
return max(max(DP(a.x,b.x),DP(a.y,b.x)),max(DP(a.x,b.y),DP(a.y,b.y)));
}
}f[N][2];
int sta[N],top;
void dfs_dp(int x)
{
f[x][0]=f[x][1]=DP();if(col[x]!=-1)f[x][col[x]]=DP(x,x);
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;dfs_dp(to1);
for(int j=0;j<2;j++)ans=max(ans,(f[x][j]*f[to1][!j]).len-(dis[x]<<1));
for(int j=0;j<2;j++)f[x][j]=f[x][j]+f[to1][j];
}head[x]=-1;
// printf("%d %d %d %lld %d %d %lld\n",x,f[x][0].x,f[x][0].y,f[x][0].len,f[x][1].x,f[x][1].y,f[x][1].len);
}
void build()
{
sort(q+1,q+size+1,cmp);cnt=top=0;sta[++top]=1;
for(int i=1;i<=size;i++)
{
int x=q[i],lca=get_lca(x,sta[top]);
while(top&&dep[lca]<=dep[sta[top-1]])add(sta[top-1],sta[top]),top--;
if(sta[top]!=lca)add(lca,sta[top]),sta[top]=lca;if(sta[top]!=x)sta[++top]=x;
}while(top>1)add(sta[top-1],sta[top]),top--;dfs_dp(1);
for(int i=1;i<=size;i++)col[q[i]]=-1,val[q[i]]=0ll;
}
} namespace T1
{
const int N = 200005;
struct node{int to,next;ll val;}e[N<<1];
int head[N],cnt,tot;vector<int>v[N];vector<ll>va[N];
inline void add_edge(int x,int y,ll z){v[x].push_back(y),va[x].push_back(z);}
inline void add(int x,int y,ll z){e[cnt]=(node){y,head[x],z};head[x]=cnt++;}
int vis[N],siz[N],mx,rot,sn;//0 fa 1 ls 2 rs
void dfs_change(int x,int from)
{
int tmp=0,lst=x,lim=v[x].size();
for(int i=0;i<lim;i++)
{
int to1=v[x][i];
if(to1!=from)
{
if(tmp&&(tmp!=lim-(x==1)))++tot,add(lst,tot,0ll),add(tot,lst,0ll),lst=tot;
tmp++,add(lst,to1,va[x][i]),add(to1,lst,va[x][i]);
dfs_change(to1,x);
}
}
}
void get_root(int x,int from)
{
siz[x]=1;
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from&&!vis[i>>1])
{
get_root(to1,x);siz[x]+=siz[to1];
if(max(siz[to1],sn-siz[to1])<mx)rot=i,mx=max(siz[to1],sn-siz[to1]);
}
}
}
int flg=0;
void calc(int x,int from,ll dis)
{
if(x<=n)q[++size]=x,col[x]=flg,val[x]=dis;siz[x]=1;
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from&&!vis[i>>1])calc(to1,x,dis+e[i].val),siz[x]+=siz[to1];
}
}
void dfs(int x)
{
mx=1<<30;sn=siz[x];rot=0;get_root(x,0);
if(!rot)return ;vis[rot>>1]=1;//printf("%d %d\n",e[rot].to,e[rot^1].to);
size=0;flg=0;calc(e[rot].to,0,0);flg=1;calc(e[rot^1].to,0,e[rot].val);
T2::build();int tmp=e[rot^1].to;dfs(e[rot].to);dfs(tmp);
}
void init()
{
memset(head,-1,sizeof(head));tot=n;ll z;
for(int i=1,x,y;i<n;i++)x=rd(),y=rd(),z=Rd(),add_edge(x,y,z),add_edge(y,x,z);
T2::init();T3::init();dfs_change(1,0);siz[1]=tot;dfs(1);
}
} int main(){scanf("%d",&n);T1::init();printf("%lld\n",ans);}

[WC2018] 州区划分

其实只要会如何判断是否有欧拉回路即可拿到$44$分

显然,DP方程:$f[S]=\sum\limits_{s\sub S}f[s]\times g[S - s]$,其中$f[s]$表示$s$这些点构成的点集的方案数,$g[s]$表示$[check (s)]\sum\limits_{i\in s}w_i$

这个东西跑$3^{21}$即使是$10s$开$O2$,也是不太现实的

那么就需要用到FWT的高级操作了...

显然,可以把枚举子集看做两个集合的交集为这个集合,也就是集合取或。

但是这样显然会有重复的情况,那么就需要用一点别的思想:设$f[i][S]$表示用$i$个$1$组成$S$的方案数。

显然,第一感觉这个东西没啥用处,但是可以这样:$f[i][S]=[x|y==S]\sum\limits_{j=1}^i f[j][x]\times g[i-j][y]$

可以看出,这样$x,y$之间就不存在交集,也就是说,将重复的情况完全去掉了。

代码.jpg

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 22
#define M 1<<21
#define ll long long
#define mod 998244353
int w[N],n,m,p,map[N][N],d[N],q[N],vis[N],tims;
int f[N][M],g[N][M],cnt[M],s[M];
int q_pow(int x,int n){int ret=1;for(;n;n>>=1,x=(ll)x*x%mod)if(n&1)ret=(ll)ret*x%mod;return ret;}
bool check(int S)
{
memset(d,0,sizeof(d));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(map[i][j]&&((1<<i-1)&S)&&((1<<j-1)&S))d[i]++;
int l=0,r=0;tims++;
for(int i=1;i<=n;i++)if(((1<<i-1)&S)&&(d[i]&1))return 0;
for(int i=1;i<=n;i++)if((1<<i-1)&S){q[r++]=i;vis[i]=tims;break;}
while(l<r)
{
int x=q[l++];
for(int i=1;i<=n;i++)if(vis[i]!=tims&&map[x][i]&&((1<<i-1)&S))vis[i]=tims,q[r++]=i;
}
return r==cnt[S];
}
void FWT(int *a,int len,int opt)
{
for(int k=2;k<=len;k<<=1)
for(int i=0,t=k>>1;i<len;i+=k)
for(int j=i;j<i+t;j++)
if(opt==1)a[j+t]=(a[j+t]+a[j])%mod;
else a[j+t]=(a[j+t]-a[j]+mod)%mod;
}
int main()
{
scanf("%d%d%d",&n,&m,&p);
for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),map[x][y]=map[y][x]=1;
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
f[0][0]=1;
int len=1<<n;
for(int S=1;S<len;S++)
{
cnt[S]=cnt[S>>1]+(S&1);
for(int i=1;i<=n;i++)if(S&(1<<i-1))s[S]+=w[i];
if(p==0)s[S]=1;
else if(p==2)s[S]=(ll)s[S]*s[S]%mod;
if(!check(S))g[cnt[S]][S]=s[S];
s[S]=q_pow(s[S],mod-2);f[0][S]=1;
}
for(int i=1;i<=n;i++)FWT(g[i],len,1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
for(int S=0;S<len;S++)
f[i][S]=(f[i][S]+(ll)f[i-j][S]*g[j][S])%mod;
FWT(f[i],len,-1);
for(int S=0;S<len;S++)
if(cnt[S]!=i)f[i][S]=0;
else f[i][S]=(ll)f[i][S]*s[S]%mod;
if(i!=n)FWT(f[i],len,1);
}
printf("%d\n",f[n][len-1]);
}

[WC2018] 即时战略

讲道理,我觉得难点在链上...

可以看出,他的询问次数基本在$n\log n$级别,(除了链.jpg

那么考虑暴力如何处理:显然是每次随机找到一个点,然后向下拓展即可。

这样的询问复杂度为$n^2$级别的(我觉得高级一点的随机化能拿很高分.jpg

然后显然你发现,这个东西的确切询问次数为:$\sum\limits_{i=1}^ndep_i$

这个东西,你想优化就很简单了!

考虑,每次只会查询$\log n$次,那么可以用点分树或者LCT(随机剖分.jpg

每次最多跳$\log n$层,因为每次跳的时候,要么跳一整条链,要么在$Splay$上跳$\log n$次。

那么就可以找到未遍历到的节点啦!

找到后暴力拓展的复杂度显然正确.jpg

那么对于链来说,相对麻烦一些,每次随机的找一个未遍历的点,分别对左右两端拓展,如果往左没被遍历就往左,否则一定在右侧。

对于这样的东西,他的期望错误次数在$\log n$以内,多随机打乱两次就好了嘛...(点黑谁也不能怪.jpg

代码!

#include "rts.h"
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 300005
#define ls ch[rt][0]
#define rs ch[rt][1]
#define get(rt) (ch[f[rt]][0]!=rt)
#define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
int ch[N][2],f[N],vis[N],idx[N],mn[N],mx[N];
inline void PushUp(int rt)
{
mn[rt]=mx[rt]=rt;
if(ls)mn[rt]=mn[ls];
if(rs)mx[rt]=mx[rs];
}
inline void rotate(int rt)
{
int x=f[rt],y=f[x],k=get(rt);
if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
ch[x][k]=ch[rt][!k];f[ch[x][k]]=x;
ch[rt][!k]=x;f[x]=rt;f[rt]=y;PushUp(x);PushUp(rt);
}
inline void Splay(int rt)
{
for(;!isroot(rt);rotate(rt))
if(!isroot(f[rt]))rotate(get(f[rt])==get(rt)?f[rt]:rt);
}
inline void access(int rt){int t=0;while(rt)Splay(rt),rs=t,PushUp(rt),t=rt,rt=f[rt];}
inline int get_root(int rt){while(!isroot(rt))rt=f[rt];return rt;}
void play(int n,int T,int dataType)
{
for(int i=2;i<=n;i++)idx[i]=i;
random_shuffle(idx+2,idx+n+1);
if(dataType==3)
{
vis[1]=1;int l=1,r=1;
for(int i=2;i<=n;i++)
{
int x=idx[i],now;if(vis[x])continue;
if(!vis[now=explore(l,x)])
{
while(now!=x)vis[now]=1,now=explore(now,x);
vis[x]=1;l=x;
}else
{
now=explore(r,x);
while(now!=x)vis[now]=1,now=explore(now,x);
vis[x]=1;r=x;
}
}
return;
}
else
{
vis[1]=1;mn[1]=mx[1]=1;
for(int i=2;i<=n;i++)
{
if(vis[idx[i]])continue;
int x=idx[i],rt=get_root(1),ret;
while(!vis[x])
{
ret=explore(rt,x);
if(mn[rs]==ret)rt=rs;
else if(mx[ls]==ret)rt=ls;
else if(vis[ret])rt=get_root(ret);
else vis[ret]=1,mn[ret]=mx[ret]=ret,f[ret]=rt,rt=ret;
}
access(x);
}
}return;
}

WC 2018 题解的更多相关文章

  1. WC 2018/CTSC 2018/APIO 2018 游记

    (要写CTSC的时候才想起来没写WC2018,那就粗略回顾一下吧hhhhh) WC 2018(简略版): 大概和 一个宁夏和一个天津的大哥一个宿舍hhhh,字典序分宿舍真是奇妙. WC讲课真的不是人听 ...

  2. ZROI WC Round1 题解

    ZROI WC Round1 题解 Problem A 题意 一个 \(n \times m\) 格子图,一个人从左上角出发,每次向右或者向下走一格,方法如下: 如果他在最下面一排,那么他会往右行走. ...

  3. ZROI WC Round5 题解

    ZROI WC Round5 题解 Problem A 题意 给定一个长度为 \(n\) 的序列,操作是交换两个相邻的数,要求将序列变成先单调不降再单调不升,求最小操作数,注意可以完全单调不降或者完全 ...

  4. PKUSC 2018 题解

    PKUSC 2018 题解 Day 1 T1 真实排名 Link Solution 考虑对于每一个人单独算 每一个人有两种情况,翻倍和不翻倍,他的名次不变等价于大于等于他的人数不变 设当前考虑的人的成 ...

  5. Good Bye 2018题解

    Good Bye 2018题解 题解 CF1091A [New Year and the Christmas Ornament] 打完cf都忘记写题解了qwq 题意就是:给你一些黄,蓝,红的球,满足蓝 ...

  6. 【待填坑】bzoj上WC的题解

    之前在bzoj上做了几道WC的题目,现在整理一下 bzoj2115 去膜拜莫队的<高斯消元解xor方程组> bzoj2597 LCT维护MST bzoj1758 分数规划+树分治+单调队列 ...

  7. 「WC 2018」州区划分

    题目大意: 给一个无向图$G(V,E)$满足$|V|<=21$,对于某一种将$G(V,E)$划分为k个的有序集合方案,若每一个子集$G_i(V_i,E_i)$,$E_i=\{(x,y)|x\in ...

  8. 解题:WC 2018 州区划分

    题面 WC之前写的,补一补,但是基本就是学新知识了 首先可以枚举子集$3^n$转移,优化是额外记录每个集合选取的个数,然后按照选取个数从小到大转移.转移的时候先FWT成“点值”转移完了IFWT回去乘逆 ...

  9. ZOJ Monthly, March 2018 题解

    [题目链接] A. ZOJ 4004 - Easy Number Game 首先肯定是选择值最小的 $2*m$ 进行操作,这些数在操作的时候每次取一个最大的和最小的相乘是最优的. #include & ...

随机推荐

  1. 18.Odoo产品分析 (二) – 商业板块(10) – 电子商务(2)

    查看Odoo产品分析系列--目录 接上一篇Odoo产品分析 (二) – 商业板块(10) – 电子商务(1) 6. 高级属性 除了我们到目前为止已经覆盖基本选项,Odoo在产品页面还提供了一些高级选项 ...

  2. Android为TV端助力 MVP设计模式!

    实现原理: MainActivity 用来更新UI,和显示业务逻辑的结果! LoginPresenterCompl 用来处理 业务逻辑 ILoginPresenter 业务处理类抽象出来的接口 ILo ...

  3. 【软件需求工程与建模 - 小组项目】第6周 - 成果展示3 - 软件设计规格说明书V4.1

    成果展示3 - 软件设计规格说明书V4.1

  4. SQL Server中通用数据库角色权限处理

    SQL Server中通用数据库角色权限处理   最近和同事在做数据库权限清理的事情,主要是删除一些账号:取消一些账号的较大的权限等,例如,有一些有db_owner权限,我们取消账号的数据库角色db_ ...

  5. web前端(6)—— 标签的属性,分类,嵌套

    属性 HTML标签可以设置属性,属性一般以键值对的方式写在开始标签中 1.HTML标签除一些特定属性外可以设置自定义属性,一个标签可以设置多个属性用空格分隔,多个属性不区分先后顺序. 2.属性值要用引 ...

  6. web前端(5)—— 常用标签2

    以下三个不仅是常用标签了,还非常重要,所以请务必好好看,重要性从高到低: 盒模型div div标签是最常用最重要的,它可以把web页面分割成很多的小块分别管理 测试代码: <!DOCTYPE h ...

  7. 自动化测试基础篇--Selenium等待时间

    摘自https://www.cnblogs.com/sanzangTst/p/8376221.html 当你觉得你的定位没有问题,但是却直接报了元素不可见,那你就可以考虑是不是因为程序运行太快或者页面 ...

  8. python设计模式之单例模式(转)

    设计模式之单例模式 单例设计模式是怎么来的?在面向对象的程序设计中,当业务并发量非常大时,那么就会出现重复创建相同的对象,每创建一个对象就会开辟一块内存空间,而这些对象其实是一模一样的,那么有没有办法 ...

  9. 基于SurfaceView的可拖动视频控件

    视频播放控件(一) 可拖动,变换SurfaceView public class DragSurfaceView extends SurfaceView implements View.OnTouch ...

  10. openSUSE Leap 15.0 Adobe Flash Player 安装说明

    鉴于Firefox安装配置文件: mozilla_lib=file $MOZ_PROGRAM LIB=lib -bit.*(x86-|S/|PowerPC|ARM aarch64)’ &&am ...