NOIP模拟96
T1 树上排列
解题思路
是一个一眼切的题目。。。
看到题目第一眼就是 Deepinc 学长讲的可重集,无序 Hash 。
直接套上一颗线段树再加上树剖, \(nlog^2n\) 直接过,好像也可以树状数组维护。
但是 第二个 Subtask 的数据出锅是真的离大谱,节点不仅有 0 还有编号大于 n 的点。。。
本来是有首切的但是一开始多测没清空险些猝死。。。
由于可重集这道题给我的心理阴影我还是套了一个双 Hash ,然而类似于维护一个区间加和区间乘的做法貌似也是可以的。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=25e4+10;
int Tes,n,q,s[N];
int tim,dfn[N],id[N],topp[N],son[N],dep[N],siz[N],fa[N];
int tot=1,head[N],ver[N<<1],nxt[N<<1];
struct Segment_Tree
{
ull tre[N<<2],has[N],p[N],base;
#define push_up(x) tre[x]=tre[ls]+tre[rs]
void init(int lim)
{
p[0]=1; for(int i=1;i<=lim;i++) p[i]=p[i-1]*base;
for(int i=1;i<=lim;i++) has[i]=has[i-1]+p[i];
}
void build(int x,int l,int r)
{
if(l==r) return tre[x]=p[s[id[l]]],void();
int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r);
push_up(x);
}
void update(int x,int l,int r,int pos,int val)
{
if(l==r)return tre[x]=p[val],void();
int mid=(l+r)>>1;
if(pos<=mid) update(ls,l,mid,pos,val);
else update(rs,mid+1,r,pos,val);
push_up(x);
}
ull query(int x,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return tre[x];
int mid=(l+r)>>1; ull sum=0;
if(L<=mid) sum+=query(ls,l,mid,L,R);
if(R>mid) sum+=query(rs,mid+1,r,L,R);
return sum;
}
}T1,T2;
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
ull power(ull x,int y)
{
ull temp=1;
for(;y;y>>=1,x=x*x)
if(y&1) temp=temp*x;
return temp;
}
void dfs1(int x)
{
siz[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i]; if(siz[to]) continue;
fa[to]=x; dep[to]=dep[x]+1;
dfs1(to); siz[x]+=siz[to];
if(siz[to]>siz[son[x]]) son[x]=to;
}
}
void dfs2(int x,int tp)
{
topp[x]=tp; dfn[x]=++tim; id[tim]=x;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nxt[i])
if(!dfn[ver[i]])
dfs2(ver[i],ver[i]);
}
int LCA(int x,int y)
{
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]]) swap(x,y);
x=fa[topp[x]];
}
if(dep[x]>dep[y]) swap(x,y); return x;
}
int dist(int x,int y){int lca=LCA(x,y);return dep[x]+dep[y]-dep[lca]-dep[fa[lca]];}
void query(int x,int y)
{
int dis=dist(x,y); ull t1=0,t2=0;
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]]) swap(x,y);
t1+=T1.query(1,1,n,dfn[topp[x]],dfn[x]);
t2+=T2.query(1,1,n,dfn[topp[x]],dfn[x]);
x=fa[topp[x]];
}
if(dep[x]>dep[y]) swap(x,y);
if(dfn[x]<=dfn[y])
t1+=T1.query(1,1,n,dfn[x],dfn[y]),
t2+=T2.query(1,1,n,dfn[x],dfn[y]);
if(t1==T1.has[dis]&&t2==T2.has[dis]) printf("Yes\n");
else printf("No\n");
}
void solve()
{
tot=1; tim=0; memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(id,0,sizeof(id));
memset(topp,0,sizeof(topp));
memset(son,0,sizeof(son));
memset(dep,0,sizeof(dep));
memset(siz,0,sizeof(siz));
memset(fa,0,sizeof(fa));
n=read(); q=read(); dep[1]=1;
for(int i=1;i<=n;i++) s[i]=read();
for(int i=1,x,y;i<n;i++)
x=read(),y=read(),
add_edge(x,y),add_edge(y,x);
dfs1(1); dfs2(1,1); T1.build(1,1,n); T2.build(1,1,n);
while(q--)
{
int opt,x,y; opt=read(); x=read(); y=read();
if(opt==1) query(x,y);
else T1.update(1,1,n,dfn[x],y),T2.update(1,1,n,dfn[x],y);
}
}
#undef int
int main()
{
#define int long long
freopen("a.in","r",stdin); freopen("a.out","w",stdout);
T1.base=13331ull; T2.base=20; T1.init(250000); T2.init(250000);
Tes=read(); while(Tes--) solve();
return 0;
}
T2 连任
解题思路
看出来是按秩合并+可撤销并茶几了,然而都不会。。。
以时间为轴,可以判断出每一条边生效的时间区间,那么直接线段树分治。
对于每个区间中的操作直接加入并进行记录,然后在处理完这个区间以及子区间之后倒序撤回操作。
递归到叶子节点直接记录答案输出就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e5+10,mod=1e9+7;
int n,m,Ans=1,fa[N],siz[N],inv[N],ans[N];
unordered_map<int,int> mp[N];
pair<int,int> s[N];
vector< pair<int,int> > v[N<<2],typ[N<<2];
inline int find(int x)
{
if(fa[x]==x) return x;
return find(fa[x]);
}
void insert(int x,int l,int r,int L,int R,pair<int,int> temp)
{
if(L<=l&&r<=R) return v[x].push_back(temp),void();
int mid=(l+r)>>1;
if(L<=mid) insert(ls,l,mid,L,R,temp);
if(R>mid) insert(rs,mid+1,r,L,R,temp);
}
void merge(int pos,int x,int y)
{
if(find(x)==find(y)) return ;
x=find(x); y=find(y); if(siz[x]>siz[y]) swap(x,y);
Ans=Ans*inv[siz[x]]%mod*inv[siz[y]]%mod*(siz[x]+siz[y])%mod;
siz[y]+=siz[x]; fa[x]=y; typ[pos].push_back(make_pair(x,y));
}
void work_back(int pos)
{
for(int i=(int)typ[pos].size()-1;i>=0;i--)
{
int x=typ[pos][i].first,y=typ[pos][i].second;
Ans=Ans*inv[siz[y]]%mod; fa[x]=x; siz[y]-=siz[x];
Ans=Ans*siz[x]%mod*siz[y]%mod;
}
}
void solve(int x,int l,int r)
{
for(auto it:v[x]) merge(x,it.first,it.second);
if(l==r) return ans[l]=Ans,work_back(x),void();
int mid=(l+r)>>1; solve(ls,l,mid); solve(rs,mid+1,r);
work_back(x);
}
#undef int
int main()
{
#define int long long
freopen("b.in","r",stdin); freopen("b.out","w",stdout);
n=read(); m=read(); inv[1]=fa[1]=siz[1]=1;
for(int i=2;i<=n;i++) fa[i]=i,siz[i]=1,inv[i]=inv[mod%i]*(mod-mod/i)%mod;
for(int i=1,opt,x,y;i<=m;i++)
{
opt=read(); x=read(); y=read();
if(x>y) swap(x,y); s[i]=make_pair(x,y);
if(opt==1){mp[x].insert(make_pair(y,i));continue;}
insert(1,1,m,mp[x].find(y)->second,i-1,s[i]);
mp[x].erase(y);
}
for(int i=1;i<=m;i++)
if(mp[s[i].first].find(s[i].second)!=mp[s[i].first].end())
insert(1,1,m,mp[s[i].first].find(s[i].second)->second,m,s[i]);
solve(1,1,m); for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
T3 排列
解题思路
sort -> 桶排 竟然会快 0.2s 实在是有用的卡常小技巧。。
第一感觉肯定是三维偏序,但是这个东西实际上是可以转化为二维上的!!!
假设一个三元排列 \((a,b,c)\) 满足三维偏序的个数可以通过两两之间的满足二维偏序的对数求出来。
设 \((a,b),(b,c),(a,c)\) 一共有 \(M\) 对合法的二维偏序对,那么一个对 \((i,j)\) 要么被计算一次要么被计算三次。
于是满足三维偏序的对数就是 \(\dfrac{M-\frac{n\times(n-1)}{2}}{2}\) 。
那么所有已经确定的数之间的贡献就有了,我们可以对于每一个已经确定的数字计算出它前后的 -1 的期望贡献。
同时也要算一下 -1 与 -1 之间的贡献。
code
#include<bits/stdc++.h>
// #define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e6+10,mod=998244353;
int n,cnt,ans,inv,inv2,sta[N];
struct Node{int a,b,c;}s[N],t[N];
bool vis[N];
struct BIT
{
int tre[N];
#define lowbit(x) (x&(-x))
inline void clear(){memset(tre,0,sizeof(tre));}
inline void insert(register int x){for(register int i=x;i<=n;i+=lowbit(i))tre[i]++;}
inline int query(register int x){register int sum=0;for(register int i=x;i;i-=lowbit(i))sum+=tre[i];return sum;}
}T;
inline int power(register int x,register int y,register int p=mod)
{
int temp=1;
for(;y;y>>=1,x=1ll*x*x%p)
if(y&1) temp=1ll*temp*x%p;
return temp;
}
#define add(x,y) x=(x+y)%mod
inline bool comp(Node x,Node y){return x.b<y.b;}
// #undef int
int main()
{
// #define int long long
freopen("c.in","r",stdin); freopen("c.out","w",stdout);
n=read(); inv2=(mod+1)>>1; for(register int i=1,x,y;i<=n;i++) s[i].b=read();
for(register int i=1;i<=n;i++) s[i].a=i,s[i].c=read(),vis[s[i].c==-1?0:s[i].c]=true;
for(register int i=1;i<=n;i++) T.insert(s[i].b),add(ans,T.query(s[i].b-1)); T.clear();
for(register int i=1;i<=n;i++) if(!vis[i]) sta[++cnt]=i; inv=power(cnt,mod-2);
for(register int i=1,tot=0;i<=n;i++)
{
if(s[i].c!=-1) T.insert(s[i].c),add(ans,T.query(s[i].c-1));
register int pos=upper_bound(sta+1,sta+cnt+1,s[i].c)-sta-1;
if(s[i].c!=-1) add(ans,1ll*pos*inv%mod*tot%mod),add(ans,1ll*(cnt-tot)*inv%mod*(cnt-pos)%mod);
else add(ans,1ll*tot*inv2%mod); tot+=s[i].c==-1;
}
T.clear(); for(int i=1;i<=n;i++) t[s[i].b]=s[i];
for(register int i=1,tot=0;i<=n;i++)
{
if(t[i].c!=-1) T.insert(t[i].c),add(ans,T.query(t[i].c-1));
register int pos=upper_bound(sta+1,sta+cnt+1,t[i].c)-sta-1;
if(t[i].c!=-1) add(ans,1ll*pos*inv%mod*tot%mod),add(ans,1ll*(cnt-tot)*inv%mod*(cnt-pos)%mod);
else add(ans,1ll*tot*inv2%mod); tot+=t[i].c==-1;
}
printf("%lld",1ll*(1ll*ans-1ll*(n-1)*n%mod*inv2%mod+mod)%mod*inv2%mod);
return 0;
}
T4 追逐
解题思路
一个最优的策略显然是把 qjd 逼到一个叶子节点之后,把之后需要的操作全部做了再把他放出来。
那么我们需要计算出每一个节点一开始向子树中走然后绕过子树一圈之后最后回到该节点并且下一步将要走到父亲节点的最小操作次数。
这个东西直接记录一个最大值次大值树形 DP 转移即可。
显然只会让 qjd 到向下走一次,于是我们可以二分出一个最后答案。
对于一个可能的答案值 \(mid\) 计算一下当前可以操作的步数也就是多余的操作步数,如果当前节点的一个儿子的 DP 值加上之前的操作值还有祖先要堵住的节点是大于 \(mid\) 的话,这个儿子就是不合法的。
我们显然是要利用多余的操作步数将这些儿子堵住的,如果不够的话也是不合法的。
最后再判一下加上最大值的子树之后总操作数是否合法就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e6+10;
int n,root,ending,f[N],fa[N],du[N],bas[N];
int tot=1,head[N],ver[N<<1],nxt[N<<1];
bool vis[N];
void add_edge(int x,int y)
{
ver[++tot]=y; du[y]++;
nxt[tot]=head[x]; head[x]=tot;
}
void dfs(int x)
{
int mx=0,sec=0;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i]; if(to==fa[x]) continue;
fa[to]=x; dfs(to);
if(mx<f[to]) sec=mx,mx=f[to];
else sec=max(sec,f[to]);
}
f[x]=sec+du[x]-1+(!fa[x]);
}
void dfs2(int x)
{
if(x==root) bas[x]=0; else bas[x]+=du[x];
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i]; if(to==fa[x]) continue;
bas[to]+=bas[x]; dfs2(to);
}
}
bool check(int val)
{
int now=ending,use=0,hav=1,mx=0;
while(now!=root)
{
int cnt=0;
for(int i=head[now];i;i=nxt[i])
if(ver[i]!=fa[now]&&!vis[ver[i]])
if(use+f[ver[i]]+bas[now]>val) cnt++;
else mx=max(mx,f[ver[i]]);
if(hav<cnt) return false;
use+=cnt; hav-=cnt; now=fa[now]; hav++;
}
return use+mx<=val;
}
#undef int
int main()
{
#define int long long
freopen("d.in","r",stdin); freopen("d.out","w",stdout);
n=read(); root=read(); ending=read();
for(int i=1,x,y;i<n;i++)
x=read(),y=read(),
add_edge(x,y),add_edge(y,x);
dfs(root); int x=ending; while(x){vis[x]=true;x=fa[x];}
for(int i=1;i<=n;i++)
if(vis[i])
for(int j=head[i];j;j=nxt[j])
bas[ver[j]]--;
int l=0,r=n,ans; dfs2(root);
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
printf("%lld",ans); return 0;
}
NOIP模拟96的更多相关文章
- NOIP模拟96(多校29)
T1 子集和 解题思路 大概是一个退背包的大白板,然而我考场上想复杂了,竟然还用到了组合数. 但是大概意思是一样的,有数的最小值一定是一个在 \(a\) 数组中存在的数字. 那么我们想办法除去它对应的 ...
- NOIP 模拟4 T2
本题属于二和一问题 子问题相互对称 考虑对于问题一:知a求b 那么根据b数组定义式 显然能发现问题在于如何求dis(最短路) 有很多算法可供选择 dijsktra,floyed,bfs/dfs,spf ...
- NOIP模拟赛20161022
NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...
- contesthunter暑假NOIP模拟赛第一场题解
contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...
- NOIP模拟赛 by hzwer
2015年10月04日NOIP模拟赛 by hzwer (这是小奇=> 小奇挖矿2(mining) [题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿 ...
- 大家AK杯 灰天飞雁NOIP模拟赛题解/数据/标程
数据 http://files.cnblogs.com/htfy/data.zip 简要题解 桌球碰撞 纯模拟,注意一开始就在袋口和v=0的情况.v和坐标可以是小数.为保险起见最好用extended/ ...
- 队爷的讲学计划 CH Round #59 - OrzCC杯NOIP模拟赛day1
题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的讲学计划 题解:刚开始理解题意理解了好半天,然后发 ...
- 队爷的Au Plan CH Round #59 - OrzCC杯NOIP模拟赛day1
题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的Au%20Plan 题解:看了题之后觉得肯定是DP ...
- 队爷的新书 CH Round #59 - OrzCC杯NOIP模拟赛day1
题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的新书 题解:看到这题就想到了 poetize 的封 ...
- CH Round #58 - OrzCC杯noip模拟赛day2
A:颜色问题 题目:http://ch.ezoj.tk/contest/CH%20Round%20%2358%20-%20OrzCC杯noip模拟赛day2/颜色问题 题解:算一下每个仆人到它的目的地 ...
随机推荐
- SharePreferences概念
概念 SharePreferences是一种轻量级的数据存储方式,它是以key-value的形式保存在 data/data//shared_prefs 下的xml文件中.通常使用它来保存应用中的一些简 ...
- VulnHub-ical打靶记录
这绝对是最简单的一个题目了. 目标发现 netdiscover -r 192.168.0.10/24 根据靶场和本地系统的网段进行扫描. 信息收集 nmap -sV -Pn -sT -sC -A 19 ...
- leetcode:1337. 方阵中战斗力最弱的 K 行
1337. 方阵中战斗力最弱的 K 行 给你一个大小为 m * n 的方阵 mat,方阵由若干军人和平民组成,分别用 0 和 1 表示. 请你返回方阵中战斗力最弱的 k 行的索引,按从最弱到最强排序. ...
- 初探Mysql架构和InnoDB存储引擎
前言 mysql相信大家都不陌生了,分享之前我们先思考几个面试题: 1.undo log和redo log了解过吗?它们的作⽤分别是什么? 2.redo log是如何保证事务不丢失的? 3.mysql ...
- 蚂蚁一面:GC垃圾回收时,内存分配和回收策略有哪些?
文章首发于公众号:腐烂的橘子 蚂蚁面试主要为电话面试,期间也会要求使用编辑器手写算法题.作为一线互联网大厂,Java 基础知识是必备的,其中垃圾回收也是面试过程中的重中之重. Java 内存的自动管理 ...
- Dubbo Mesh:从服务框架到统一服务控制平台
简介: Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java.Golang 等多语言 SDK 实现. 作者:Dubbo 社区 Ap ...
- 说说关系型数据库与Serverless
简介: 看到如今Serverless在云计算行业喷薄欲出的态势,像极了<星星之火,可以燎原>中的描述:虽然不能预测未来的发展和变化,但对于云计算来说这是个相对确定的方向.本文从产业界和学术 ...
- 持续定义Saas模式云数据仓库+实时分析
简介: 从实时分析的价值.场景和数据流程,以及用户对平台能力要求展开,讲述云数据仓库MaxCompute的产品能力优势 ,面对实时分析场景的能力演进要求.进而以实时分析典型场景的全数据流程处理.建模和 ...
- 最佳实践丨构建云上私有池(虚拟IDC)的5种方案详解
简介:云上私有池系列终篇终于来了,本文将重点介绍构建云上的私有池(虚拟IDC)的多种方案和各自的优缺点,并给出相关的性价比优化建议. 本文作者:阿里云技术专家李雨前 摘要 围绕私有池(虚拟IDC)的 ...
- 阿里云OSS文件上传几种方法(主要是前端)
目录 零.准备 一.服务端签名后直传 1. 阿里云控制台配置 2. 后端接口开发(PHP) 3. 前端获取签名后上传 二.使用STS临时凭证进行上传 1. 后端接口开发(node) 2. 前端获取临时 ...