版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
T1:
反正结论就是输入 就输出 。
我考场上用的概率生成函数+组合等式毒瘤变形推出来的,不建议大家尝试。。。
今天T1浪费的时间有点多,导致T3暴力挂掉了
证明考虑期望的线性性,在某一个维度上进行一次平移就行了。
T2:
考场上想了两个 发现有90分就不想管了,20分钟写完走人。
发现我差分一下就是正解。。。
首先还是观察性质,设
表示题目给出的两棵树,
中的一条边对
中的一条边有贡献当且仅当它们互相都在对方的覆盖路径上。(看不懂不要问,反正我自己懂,题解上面也有解释
于是把 的边放到 上树上差分,然后询问一下 中的这条边在 中覆盖的路径上有多少点存活。
单点加,链求和,差分一下转化为子树加,单点求和,由于我们只需要子树内部的信息,在dfs的时候利用时间差分一次即可,于是可以用树状数组小常数维护。听说std写的线段树合并MLE了
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T get(){
char c;T num;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int gi(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
typedef std::pair<int,int> pii;
#define fi first
#define se second
int n;
namespace T2{
cs int N=1e6+7;
int el[N],nxt[N<<1],to[N<<1],ec;
inline void adde(int u,int v){
nxt[++ec]=el[u],el[u]=ec,to[ec]=v;
nxt[++ec]=el[v],el[v]=ec,to[ec]=u;
}
int in[N],out[N],dfn;
int fa[N],d[N],son[N],siz[N],top[N];
void dfs1(int u,int p){
fa[u]=p,d[u]=d[p]+1;
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=p){
dfs1(v,u),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}++siz[u];
}
void dfs2(int u,int tp){
top[u]=tp;in[u]=++dfn;
if(son[u])dfs2(son[u],tp);
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=fa[u]&&v!=son[u])dfs2(v,v);out[u]=dfn;
}
inline int LCA(int u,int v){
while(top[u]!=top[v])d[top[u]]<d[top[v]]?v=fa[top[v]]:u=fa[top[u]];
return d[u]<d[v]?u:v;
}
void init(){dfs1(1,0);dfs2(1,1);}
}
namespace BIT{
cs int N=1e6+7;int tr[N];
inline void add(int p,int v){for(;p<=n;p+=p&-p)tr[p]+=v;}
inline int query(int p){int r=0;for(;p;p&=p-1)r+=tr[p];return r;}
}
namespace T1{
cs int N=1e6+7;
int el[N],nxt[N<<1],to[N<<1],ec=1;
inline void adde(int u,int v){
nxt[++ec]=el[u],el[u]=ec,to[ec]=v;
nxt[++ec]=el[v],el[v]=ec,to[ec]=u;
}
int fa[N],d[N],son[N],siz[N],top[N];
void dfs1(int u,int p){
fa[u]=p,d[u]=d[p]+1;
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=p){
dfs1(v,u),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}++siz[u];
}
void dfs2(int u,int tp){
top[u]=tp;
if(son[u])dfs2(son[u],tp);
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=fa[u]&&v!=son[u])dfs2(v,v);
}
inline int LCA(int u,int v){
while(top[u]!=top[v])d[top[u]]<d[top[v]]?v=fa[top[v]]:u=fa[top[u]];
return d[u]<d[v]?u:v;
}
void init(){dfs1(1,0);dfs2(1,1);}
int ans[N];
std::vector<int> ins[N],del[N];
void work(int u,int p,int eid){
int lca;
if(eid){
lca=T2::LCA(u,p);
ans[eid]=BIT::query(T2::in[u])+BIT::query(T2::in[p])-BIT::query(T2::in[lca])*2;
}
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=p)work(v,u,e>>1);
for(int re i=0;i<ins[u].size();++i){
BIT::add(T2::in[ins[u][i]],1);
BIT::add(T2::out[ins[u][i]]+1,-1);
}
for(int re i=0;i<del[u].size();++i){
BIT::add(T2::in[del[u][i]],-2);
BIT::add(T2::out[del[u][i]]+1,2);
}
if(eid){
ans[eid]=BIT::query(T2::in[u])+BIT::query(T2::in[p])-BIT::query(T2::in[lca])*2-ans[eid];
}
}
void solve(){
init();
for(int re i=2;i<=n;++i){
ins[i].push_back(i);
ins[T2::fa[i]].push_back(i);
del[LCA(i,T2::fa[i])].push_back(i);
}
work(1,0,0);
for(int re i=1;i<n;++i)cout<<ans[i]<<" ";
}
}
signed main(){
#ifdef zxyoi
freopen("road.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
freopen("road.in","r",stdin);freopen("road.out","w",stdout);
#endif
#endif
int size=40<<20;//40M
//__asm__ ("movl %0, %%esp\n"::"r"((char*)malloc(size)+size));//调试用这个
__asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));//提交用这个
n=gi();
for(int re i=1;i<n;++i)T1::adde(gi(),gi());
for(int re i=1;i<n;++i)T2::adde(gi(),gi());
T2::init();T1::solve();
exit(0);
}
T3:
非常诡异的倍增,做法自己看题解,主要是细节有点多,这里扔一个代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
#define int ll
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T get(){
char c;T num;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int gi(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second
cs int N=2e5+7,INF=1e9+1e7;
int n,m,Q;
std::vector<int> G[N];
std::vector<int> pre[N];
int siz[N],rk[20][N];
int to[20][N],son[N],lim[N];
int d[N],q[N],qn;
int go(int s,int k){
if(!k)return s;
for(int re i=lim[s];~i;--i){
if(rk[i][s]<=k&&rk[i][s]+siz[to[i][s]]>=k)
return go(to[i][s],k-rk[i][s]);
}
int p=std::lower_bound(pre[s].begin(),pre[s].end(),k)-pre[s].begin();
return go(G[s][p],k-(p?pre[s][p-1]:0)-1);
}
signed main(){
#ifdef zxyoi
freopen("treasure.in","r",stdin);
#endif
n=gi(),m=gi();
for(int re i=1;i<=m;++i){
int u=gi(),v=gi();++d[v];
G[u].push_back(v);
}
for(int re i=1;i<=n;++i)if(!d[i])q[++qn]=i;
for(int re i=1;i<=qn;++i){
int u=q[i];for(int v:G[u])if(!--d[v])q[++qn]=v;
}
for(int re i=qn;i;--i){
int u=q[i];ll rec=0;
for(int re v:G[u]){
if(siz[v]>siz[son[u]])son[u]=v,rec=siz[u]+1;
siz[u]+=siz[v]+1;siz[u]=std::min(siz[u],INF);
pre[u].push_back(siz[u]);
}to[0][u]=son[u],rk[0][u]=rec;
for(int re &j=(lim[u]=0);to[j][u];++j){
to[j+1][u]=to[j][to[j][u]];
rk[j+1][u]=rk[j][to[j][u]]+rk[j][u];
rk[j+1][u]=std::min(rk[j+1][u],INF);
}--lim[u];
}Q=gi();
while(Q--){
int s=gi(),k=gi();
if(k>siz[s]){cout<<"-1\n";continue;}
cout<<go(s,k)<<"\n";
}
return 0;
}
吐槽:
怎么说呢,今天算是最近几场里面翻车比较大的一次,T3暴力挂成了0分。
然而这是因为T1用了非常毒瘤的推法推了一个半小时,然后后面时间就感觉有点不够用了,前3分钟T3才调过样例,然而样例太弱挂成0分。。。
L:你简单题做少了
我:。。。
我TM还能说什么。。。