NOIP Day 1 题解Sengxians Blog.docx
- 文档编号:4914808
- 上传时间:2022-12-11
- 格式:DOCX
- 页数:14
- 大小:21.86KB
NOIP Day 1 题解Sengxians Blog.docx
《NOIP Day 1 题解Sengxians Blog.docx》由会员分享,可在线阅读,更多相关《NOIP Day 1 题解Sengxians Blog.docx(14页珍藏版)》请在冰豆网上搜索。
NOIPDay1题解SengxiansBlog
NOIP2016Day1题解Sengxian'sBlog
先声明一下,如果你是初学者,你可能会看不懂其中的一些东西,原因是你的知识点以及技巧没有跟上,我会尽量写得详细一点,如果还有不懂,欢迎留言。
玩具谜题toy知识点模拟
分析可以发现,本题就是根据要求在环上顺时针或者逆时针走动,那么假设当前的位置是ppp,那么逆时针走xxx个到达的位置是(p?
1x)modn1(p-1x)\bmodn1(p?
1x)modn1,顺时针走xxx个到达的位置是(p?
1?
x)modn1(p-1-x)\bmodn1(p?
1?
x)modn1。
模拟每一次的指令,判断每次是走顺时针还是逆时针即可。
实现上要注意,C中对于模运算的定义与数学中的模运算是不同的,我们知道在数学上amodb=a?
b?
?
ab?
a\bmodb=a-b\cdot\lfloor\fracab\rflooramodb=a?
b?
?
?
b?
?
a?
?
?
,问题就在于?
ab?
\lfloor\fracab\rfloor?
?
b?
?
a?
?
?
的计算上。
在大部分编译器中,两个int相除,是直接丢弃小数位得到答案的,这就导致了在C中两个int相除并非总是得到?
ab?
\lfloor\fracab\rfloor?
?
b?
?
a?
?
?
(例如?
32=?
1\frac{-3}2=-1?
2?
?
?
3?
?
=?
1),所以aaa为负数的情况下,取模会存在问题,amodba\bmodbamodb不一定能够得到一个非负整数,但是我们可以保证这个数在(?
∣b∣,∣b∣)(-\vertb\vert,\vertb\vert)(?
∣b∣,∣b∣)内,在所以实际需要取模的时候,采用((amodb)b)modb((a\bmodb)b)\bmodb((amodb)b)modb来得到非负整数。
代码#include
usingnamespacestd;
constintMAX_N=1000003,MAX_LEN=103;
intn,m,dir[MAX_N];
charstr[MAX_N][MAX_LEN];
intmain(){
scanf('%d%d',&n,&m);
for(inti=0;in;i)
scanf('%d%s',diri,str[i]);
intpos=0;
for(inti=0,d,a;im;i){
scanf('%d%d',&d,&a);
intdd=d^dir[pos];
if(dd==1)(pos=a)%=n;
else(pos-=a)%=n;
pos=(pos%nn)%n;
}
printf('%s\n',str[pos]);
return0;
}
天天爱跑步running考点DFS,LCA(最近公共祖先),差分标记,前缀和
分析对于NOIP初学者来说,这个题在考场上是比较没有办法A掉的,但是这并不妨碍我们拿到暴力分。
25分对于前5个测试点,n,m≤1000n,m\le1000n,m≤1000。
可以考虑对于每一个人(u,v)(u,v)(u,v),都使用BFS找出u→vu\rightarrowvu→v的路径,然后更新路径上有满足条件的点即可。
至于找出路径的方法,只需要从uuu开始BFS,并且在BFS中从aaa扩展到bbb时,记录aaa的前继为bbb,最后我们从vvv开始,不断地找前继,最终到uuu,这样就还原出了路径,你可能还需要记录一个到点vvv的距离来帮助判断路径上的点是否满足条件。
复杂度:
O(n2)O(n^2)O(n?
2?
?
)。
40分接下来3个测试点,n,m≤105n,m\le{10}^5n,m≤10?
5?
?
,形成一条链。
本题有2s的时限,大概能够承受108{10}^810?
8?
?
的运算量,如果使用25分算法,运算量将达到1010{10}^{10}10?
10?
?
,绝对无法承受。
所以我们不能采用还原路径的方法。
由于链上的点依次是,而题目中的路径有从编号小的点走到编号大的点或者从编号大的点走到编号小的点。
我们将这两种路径分开考虑,先考虑从编号小的点走到编号大的点(u,v)(u≤v)(u,v)(u\lev)(u,v)(u≤v)。
对于(u,v)(u,v)(u,v),如果路径上的点i(u≤i≤v)i(u\lei\lev)i(u≤i≤v)要满足条件的话,必然是满足wi=i?
uw_i=i-uw?
i?
?
=i?
u,我们不妨设w′i=wi?
i{w'}_i=w_i-iw?
′?
?
?
i?
?
=w?
i?
?
?
i,那么现在的条件转化为了w′i=?
u{w'}_i=-uw?
′?
?
?
i?
?
=?
u,也就是说,路径上的点iii答案1的充分必要条件是满足w′i{w'}_iw?
′?
?
?
i?
?
等于一个定值。
现在问题转化成了支持两种操作:
将一段链上的每个点插入一个数。
查询点iii上有多少个数是w′i{w'}_iw?
′?
?
?
i?
?
。
我们考虑使用差分化标记实现,这个思想在NOIP2012借教室出现过。
对于将链上的[l,r)[l,r)[l,r)同时插入一个数vvv,差分后转化为在lll位置插入一个(v,1)(v,1)(v,1)(表示增加一个vvv),在rrr位置插入一个(v,?
1)(v,-1)(v,?
1)(表示删除一个vvv)。
由于差分的逆运算是前缀和,我们顺序遍历这条链,维护cntv\mathrm{cnt}_vcnt?
v?
?
为数vvv出现的次数(由于iii有负数,所以我们考虑将这个数组向右平移nnn个单位来方便存储),每遍历到一个点iii,我们就处理它上面所有的标记(v,t)(v,t)(v,t),将cntv\mathrm{cnt}_vcnt?
v?
?
加上ttt。
这样遍历到每个点的时候,cntv\mathrm{cnt}_vcnt?
v?
?
就是在它上面数vvv的出现次数。
对于反过来的路径(u,v)(u,v)(u,v),同理可以发现点i(v≤i≤u)i(v\lei\leu)i(v≤i≤u)答案1的充分必要条件是wi=u?
iw_i=u-iw?
i?
?
=u?
i,设w′′i=w?
(n?
i){w''}_i=w-(n-i)w?
′′?
?
?
i?
?
=w?
(n?
i),那么条件转化为w′′i=u?
n{w''}_i=u-nw?
′′?
?
?
i?
?
=u?
n,也是一个常数,可以用类似的方法处理。
复杂度:
O(nm)O(nm)O(nm)。
这个做法比较重要,对于初学者建议实现这个程序。
树链剖分刚刚我们得出了链上的算法,树链剖分可以让我们将树剖分成链,这样树上的问题就可以转化为链上的问题,套用链上的算法即可。
每条路径产生O(logn)O(\logn)O(logn)个标记,处理每条路径的复杂度也是O(logn)O(\logn)O(logn),最后结算标记的复杂度O(nlogn)O(n\logn)O(nlogn),所以总复杂度O(nlogn)O(n\logn)O(nlogn)。
依据实现的情况,得到95或100分。
100分NOIP显然不会考树链剖分,这题有更优美的解法。
我们首先求出每条路径的LCA,使用Tarjan算法在O(nm)O(nm)O(nm)的时间内预处理出LCA。
对于每一条路径,我们拆成直上直下的两条链(如果本身就是这种链就不拆),设did_id?
i?
?
为iii点的深度。
我们考虑链u→vu\rightarrowvu→v,对于向下的链,路径上满足条件的点iii必须满足di?
du=wid_i-d_u=w_id?
i?
?
?
d?
u?
?
=w?
i?
?
,即wi?
diw_i-d_iw?
i?
?
?
d?
i?
?
是一个定值。
对于向上的链,有du?
di=wid_u-d_i=w_id?
u?
?
?
d?
i?
?
=w?
i?
?
,即widiw_id_iw?
i?
?
d?
i?
?
是一个定值,问题同样转化为了支持两种操作(对于两种链分别考虑,这里只说了第一种链):
将一段路径(没有弯折)上的每个点插入一个数。
查询点iii上有多少个数是w′i{w'}_iw?
′?
?
?
i?
?
。
如果在路径u→v(du≤dv)u\rightarrowv(d_u\led_v)u→v(d?
u?
?
≤d?
v?
?
)上插入一个数xxx,可以差分转化为在vvv这个地方插入一个xxx,在fau\mathrm{fa}_ufa?
u?
?
这个地方删除一个数xxx,则点iii上的数就是其子树的和(一个常用技巧,建议画图验证)。
显然对于每一个点都求一次子树是O(n2)O(n^2)O(n?
2?
?
)的,考虑如何快速计算。
我们可以弄出树的DFS序,对于一棵子树,它在DFS序中必然是连续的一段。
那么对于一个点iii,我们只需要查询它的子树代表的一段连续的区间中有多少个数w′i{w'}_iw?
′?
?
?
i?
?
。
假设其子树的区间为[l,r][l,r][l,r],Tj,vT_{j,v}T?
j,v?
?
为[0,r][0,r][0,r]中有多少个vvv,那么iii的答案等于Tr,w′i?
Tl?
1,w′iT_{r,{w'}_i}-T_{l-1,{w'}_i}T?
r,w?
′?
?
?
i?
?
?
?
?
T?
l?
1,w?
′?
?
?
i?
?
?
?
。
我们先算出所有点的lll和rrr,然后离线,顺序遍历DFS序列来结算每个前缀和。
这一步比较抽象,大体的思想就是每遍历到一个位置iii,结算所有包含TiT_iT?
i?
?
项的区间,由于只有2n2n2n个区间,所以结算的复杂度是O(nm)O(nm)O(nm)的。
具体的实现见代码,复杂度:
O(nm)O(nm)O(nm)。
代码树链剖分//CreatedbySengxianon2016/11/23.
//Copyright(c)2016年Sengxian.Allrightsreserved.
#include
usingnamespacestd;
constintMAX_N=3000003;
structedge{
edge*next;
intto;
edge(edge*next=NULL,intto=0):
next(next),to(to){}
}pool[MAX_N*2],*pit=pool,*first[MAX_N];
intn,m,w[MAX_N],ans[MAX_N];
intdfn[MAX_N],fa[MAX_N],s[MAX_N],bel[MAX_N],dep[MAX_N],chainDep[MAX_N];
voiddfs1(intu,intf){
fa[u]=f,s[u]=1;
for(edge*e=first[u];e;e=e->next)if(e->to!
=f){
dep[e->to]=dep[u]1;
dfs1(e->to,u);
s[u]=s[e->to];
}
}
vectorint>chains[MAX_N];
voiddfs2(intu,intnum){
staticinttsp=0;
dfn[u]=tsp,bel[u]=num;
chains[num].push_back(u);
intmx=-1,id=0;
for(edge*e=first[u];e;e=e->next)
if(e->to!
=fa[u]&&s[e->to]>mx)mx=s[id=e->to];
if(mx==-1)return;
chainDep[id]=chainDep[u]1;
dfs2(id,num);
for(edge*e=first[u];e;e=e->next)
if(e->to!
=fa[u]&&e->to!
=id)chainDep[e->to]=0,dfs2(e->to,e->to);
}
typedefpairint,int>state;
vectorstate>mark1[MAX_N],mark2[MAX_N];
#definesz(x)((int)x.size())
intdis(intu,intv){
intd=0;
while(bel[u]!
=bel[v]){
if(dep[bel[u]]dep[bel[v]])swap(u,v);
d=chainDep[u]1;
u=fa[bel[u]];
}
if(dep[v]dep[u])swap(u,v);
d=chainDep[v]-chainDep[u]1;
returnd-1;
}
voidsolve(intu,intv){
intdisU=0,disV=dis(u,v),d,t;
while(bel[u]!
=bel[v]){
if(dep[bel[u]]>dep[bel[v]]){
mark1[u].push_back(state(disU-(sz(chains[bel[u]])-1-chainDep[u]),1));
disU=chainDep[u]1;
u=fa[bel[u]];
}else{
d=disV-chainDep[v];
mark2[bel[v]].push_back(state(d,1));
if(chainDep[v]1!
=sz(chains[bel[v]])){
t=chains[bel[v]][chainDep[v]1];
mark2[t].push_back(state(d,-1));
}
disV-=chainDep[v]1;
v=fa[bel[v]];
}
}
if(dep[u]dep[v]){
mark2[u].push_back(state(disU-chainDep[u],1));
if(chainDep[v]1!
=sz(chains[bel[v]])){
t=chains[bel[v]][chainDep[v]1];
mark2[t].push_back(state(disU-chainDep[u],-1));
}
}else{
mark1[u].push_back(state(disU-(sz(chains[bel[u]])-1-chainDep[u]),1));
if(v!
=bel[v]){
mark1[fa[v]].push_back(state(disU-(sz(chains[bel[u]])-1-chainDep[u]),-1));
}
}
}
staticintcnt[MAX_N*2],ti[MAX_N*2];
intnow_t=0;
inlineintget(intx){
if(ti[x]!
=now_t)ti[x]=now_t,cnt[x]=0;
returncnt[x];
}
inlinevoidadd(intx,intv){
if(ti[x]!
=now_t)ti[x]=now_t,cnt[x]=0;
cnt[x]=v;
}
voidcal(){
for(inttt=0;ttn;tt)if(bel[tt]==tt){
intlen=chains[tt].size();
now_t;
for(inti=0;ilen;i){
for(intj=0;j(int)mark2[chains[tt][i]].size();j)
add(mark2[chains[tt][i]][j].firstn,mark2[chains[tt][i]][j].second);
ans[chains[tt][i]]=get(w[chains[tt][i]]-in);
}
now_t;
for(inti=len-1;i>=0;--i){
for(intj=0;j(int)mark1[chains[tt][i]].size();j){
add(mark1[chains[tt][i]][j].firstn,mark1[chains[tt][i]][j].second);
}
ans[chains[tt][i]]=get(w[chains[tt][i]]-(len-1-i)n);
}
}
}
intmain(){
scanf('%d%d',&n,&m);
for(inti=0,u,v;in-1;i){
scanf('%d%d',&u,&v),--u,--v;
first[u]=new(pit)edge(first[u],v);
first[v]=new(pit)edge(first[v],u);
}
for(inti=0;in;i)scanf('%d',wi);
dfs1(0,-1),dfs2(0,0);
for(inti=0,u,v;im;i){
scanf('%d%d',&u,&v),u--,v--;
solve(u,v);
}
cal();
for(inti=0;in;i)printf('%d%c',ans[i],i1==n?
'\n':
'');
return0;
}
正解//CreatedbySengxianon2016/11/23.
//Copyright(c)2016年Sengxian.Allrightsreserved.
#include
usingnamespacestd;
typedeflonglongll;
inlineintreadInt(){
staticintn,ch;
n=0,ch=getchar();
while(!
isdigit(ch))ch=getchar();
while(isdigit(ch))n=n*10ch-'0',ch=getchar();
returnn;
}
constintMAX_N=3000003,MAX_M=3000003;
structedge{
edge*next;
intto;
edge(edge*next=NULL,intto=0):
next(next),to(to){}
}pool[(MAX_NMAX_M)*2],*pit=pool,*first[MAX_N],*qFirst[MAX_N];
intn,m,w[MAX_N],w1[MAX_N],w2[MAX_N];
intqueryU[MAX_M],queryV[MAX_M],queryLCA[MAX_N],queryAns[MAX_M];
intfa[MAX_N],s[MAX_N],d[MAX_N],dfn[MAX_N],seq[MAX_N];
namespacedisjoint_union{
intufa[MAX_N];
inlinevoidinit(intn){
for(inti=0;in;i)ufa[i]=i;
}
inlineintfind(intx){
returnufa[x]==x?
x:
ufa[x]=find(ufa[x]);
}
inlinevoidunite(intx,inty){
x=find(x),y=find(y);
ufa[x]=y;
}
}
usingnamespacedisjoint_union;
voiddfs(intu,intfa){
staticboolvis[MAX_N];
staticinttsp=0;
vis[u]=true,:
:
fa[u]=fa,s[u]=1;
dfn[u]=tsp,seq[tsp]=u;
for(edge*e=first[u];e;e=e->next)if(e->to!
=fa){
d[e->to]=d[u]1;
dfs(e->to,u);
unite(e->to,u);
s[u]=s[e->to];
}
for(edge*q=qFirst[u];q;q=q->next){
intv=queryU[q->to]==u?
queryV[q->to]:
queryU[q->to];
if(vis[v])queryLCA[q->to]=find(v);
}
}
typedefpairint,int>state;
vectorstate>mark1[MAX_N],mark2[MAX_N],pos[MAX_N];
intcnt1[MAX_N*4],cnt2[MAX_N*4];
inlinevoidgiveTag(intu,intv,ints,boolflag=false){
if(d[u]d[v]){
mark1[v].push_back(state(s-d[u],1));
if(fa[u]>=0)mark1[fa[u]].push_back(state(s-d[u],-1));
}else{
mark2[u].push_back(state(sd[u],1));
if(flag)mark2[v].push_back(state(sd[u],-1));
elseif(fa[v]>=0)mark2[fa[v]].push_back(state(sd[u],-1));
}
}
voidsolve(){
for(inti=0;in;i)w1[i]=w[i]-d[i];
for(inti=0;in;i)w2[i]=w[i]d[i];
for(inti=0,u,v,LCA;im;i){
u=queryU[i],v=queryV[i],LCA=queryLCA[i];
if(u==LCA||v==L
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- NOIP Day 题解 Sengxians Blog