[NOIP2018模擬賽10.20A]掛分報告
閑扯
先看看了B組,T1 ZROI剛好講過一個性質原根一般很小的,直接枚舉;T2一眼二分然後似乎狀壓 T3沒看
然後上來A組題,T1 flow這名字...網絡流?!
T1題面非常的社會主義核心價值觀,看到有個\(m==n\)的限制就想如果去掉怎麽樣,發現一棵樹的話答案是確定的,然後考慮加上那條多出來的邊,發現答案還是不變的?!想了想好像確實是這樣,你樹邊確定了環邊根本不用管,判斷有無解就是點值加起來是否為0.於是直接DFS掃一遍去掉環邊再DFS一遍就好了
T2 題面1984還行 出題人小心啊 掃了一眼覺得好難告辭
T3 第一眼題面 woc!!求求你們給國家省點子彈,我覺得博客中貼出這題題面的也要被查睡標了
第二眼woc?!這不是雅禮集訓講過的原題嗎?!還記得點思路就是預處理坐幾班車最遠可到達的地方,講題人還提到了長鏈剖分
於是肛肛肛...結果死活沒調出來...然後xxzh巨佬講了一種更好寫的暴力....感覺以後考試看到原題還是得想想有沒有其他的思路
結果15+0+0 T1 TM 正負號打反了,又犯SB錯誤 心態崩了
T1 flow
分析
某div 2 F竟這麽水(你還不是掛分了)
見閑扯
代碼
/* code by RyeCatcher */ inline char gc(){ static char buf[SIZE],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++; } #define gc getchar template <class T>inline void read(T &x){ x=0;int ne=0;char c; while((c=gc())>‘9‘||c<‘0‘)ne=c==‘-‘;x=c-48; while((c=gc())>=‘0‘&&c<=‘9‘)x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ; } const int maxn=200005; const int inf=0x7fffffff; struct Edge{ int ne,to,id; bool ok; }edge[maxn<<2]; int h[maxn],num_edge=1; inline void add_edge(int f,int to,int id){ edge[++num_edge].ne=h[f]; edge[num_edge].to=to; edge[num_edge].id=id; edge[num_edge].ok=0; h[f]=num_edge; } int w[maxn]; struct Nico{ int x,y,id,dis; }nico[maxn<<2]; struct QAQ{ int x,y,fff; int xd,yd; }yyy[maxn<<2]; int tot=0; int n,m; namespace fake{ ll ans=0; bool vis[maxn]; int fa[maxn]; void gao_cyc(int now){//去非樹邊 int v; vis[now]=1; for(ri i=h[now];i;i=edge[i].ne){ v=edge[i].to; if(v==fa[now])continue; if(vis[v]){ edge[i].ok=edge[i^1].ok=1; } if(edge[i].ok)continue; fa[v]=now; gao_cyc(v); } return ; } void get_ans(int now){ int v,id=0; for(ri i=h[now];i;i=edge[i].ne){ v=edge[i].to; if(edge[i].ok)continue; if(v==fa[now]){ id=edge[i].id;continue; } get_ans(v); } yyy[++tot].x=now,yyy[tot].y=fa[now]; yyy[tot].xd=-w[now],yyy[tot].yd=w[now]; yyy[tot].fff=id; w[fa[now]]+=w[now]; return ; } void main(){ int x,y,a,b; for(ri i=1;i<=n;i++){ ans+=w[i]; } memset(vis,0,sizeof(vis)); if(ans==0)puts("Possible"); else{ puts("Impossible"); return ; } fa[1]=0; gao_cyc(1); get_ans(1); for(ri i=1;i<=tot;i++){ x=yyy[i].x,y=yyy[i].y; a=nico[yyy[i].fff].x,b=nico[yyy[i].fff].y; //printf("%d %d %d %d\n",x,y,a,b); if(x==a&&y==b){ nico[yyy[i].fff].dis=-yyy[i].xd; } else if(x==b&&y==a){ nico[yyy[i].fff].dis=-yyy[i].yd; } } for(ri i=1;i<=m;i++){ printf("%d\n",-nico[i].dis); } return ; } } int main(){ int x,y; FO(flow); read(n); for(ri i=1;i<=n;i++)read(w[i]); read(m); for(ri i=1;i<=m;i++){ read(x),read(y); add_edge(x,y,i); add_edge(y,x,i); nico[i].x=x,nico[i].y=y,nico[i].dis=0; } //if(m<=20&&n<=20)bf::main(); fake::main(); return 0; }
T2 moon
咕
T3 car
- 前置技能點
- 倍增
- 掃描線
- dfs序
預處理每個點在一條鏈上坐\(2^j\)次車最遠到哪裏,這顯然可以倍增搞
然後考慮答案怎麽算
對於詢問\((x,y)\),求出\(z=lca(x,y)\),\(x,y\)先分別跳到距\(z\)最近的點(也就是下次就到\(z\)或更遠),這時候先統計個答案步數\(ans\)
然後發現答案只有兩種情況
Case#1
兩點分別跳一次到\(z\),最終\(ans=ans+2\)
Case#2
設這時候\(x,y\)分別跳到了\(x‘,y‘\)
有一班車覆蓋了路徑\((x‘,y‘)\),那麽答案就是\(ans=ans+1\),因為你只要坐這班車就可以越過LCA到另一個點
考慮怎麽判斷有沒有一班車覆蓋這條路徑,轉化一下變成是否有一班車\((st,ed)\),\(st\)在\(x‘\)子樹中,\(ed\)在\(y‘\)子樹中(包括\(x‘.y‘\))
如果你看過https://rye-catcher.github.io/2018/10/17/JZOI100019-A-dfs%E5%BA%8F-%E6%89%AB%E6%8F%8F%E7%BA%BF/
就會發現這還可以繼續轉化成\(dfn[x‘]<=dfn[st]<=ed[x‘],dfn[y‘]<=dfn[ed]<=ed[y‘]\)
將\((dfn[st],dfn[ed])\)看成一個坐標,發現就是判斷一個矩形中有沒有點
似乎可以在線主席樹做,也好像可以二分套二分,這裏學到了一個新操作樹狀數組+掃描線+二維前綴和
我們轉化後詢問\((x‘,y‘)\)矩形的四個坐標為\((dfn[x‘],dfn[y‘]),(dfn[x‘],ed[y‘]),(ed[x‘],dfn[y‘]),(ed[x‘],ed[y‘])\)
一條班車路徑轉化成一個點\((dfn[st],dfn[ed])\)
我們把這些點放在一起按照掃描線思路,將點按橫坐標進行排序,然後遍歷所有點(優先遍歷班車路徑轉化後的點)
如果是班車路徑的點,加入樹狀數組\([1,dfn[ed]]\)(\(dfn[ed]\)其實就是坐標(x,y)中的\(y\))前綴和
如果是個矩形想要查詢裏面點數咋辦?二維前綴和.
我們是將所有點按橫坐標排序的,所以我們可以先加上\(B\)的點數,減去\(A+B\)的點數,然後等到\(ed[x‘]\)坐標時減去\(B+C\) 點數,最後加上整個大面積就是矩形內點數
顯然這些部分直接查詢樹狀數組前綴和就好了
然後有非常多的細節,見代碼註釋吧...
對於樹上路徑,我們都默認dfs序小的為起點方便處理,也算是個技巧
/*
code by RyeCatcher
*/
inline char gc(){
static char buf[SIZE],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while((c=gc())>‘9‘||c<‘0‘)ne=c==‘-‘;x=c-48;
while((c=gc())>=‘0‘&&c<=‘9‘)x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ;
}
const int maxn=500005;
const int inf=0x7fffffff;
int n,m,q;
struct Edge{
int ne,to;
}edge[maxn<<1];
int h[maxn],num_edge=1;
inline void add_edge(int f,int to){
edge[++num_edge].ne=h[f];
edge[num_edge].to=to;
h[f]=num_edge;
}
int f[maxn][23];
int dep[maxn],fa[maxn],son[maxn],size[maxn],top[maxn],dfn[maxn],ed[maxn],tot=0;
void dfs_1(int now){
int v;size[now]=1;
//if(now==150000)printf("wtf %d\n",now,fa[now]);
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa[now])continue;
dep[v]=dep[now]+1,fa[v]=now;
dfs_1(v);
size[now]+=size[v];
if(!son[now]||size[v]>size[son[now]])son[now]=v;
}
return ;
}
void dfs_2(int now,int t){
int v;
dfn[now]=++tot,top[now]=t;
if(!son[now]){
ed[now]=tot;
return ;
}
dfs_2(son[now],t);
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa[now]||v==son[now])continue;
dfs_2(v,v);
}
ed[now]=tot;
return ;
}
inline int get_lca(int x,int y){
int xx=x,yy=y;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])std::swap(x,y);
x=fa[top[x]];
}
//if(!x)printf("--%d %d %d--\n",xx,yy,x);
if(dep[x]>dep[y])return y;
return x;
}
void pre_dfs(int now){
int v;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa[now])continue;
pre_dfs(v);
if(!f[now][0]||(f[v][0]&&dep[f[now][0]]>dep[f[v][0]]))f[now][0]=f[v][0];//特判
}
return ;
}
struct Seg{
int x,y,id,d;
bool operator <(const Seg &rhs)const{
return (x==rhs.x)?id<rhs.id:x<rhs.x;
}
}seg[maxn<<2];
int sum[maxn<<2];
inline void update(int x,int k){for(;x<=n;x+=x&(-x))sum[x]+=k;}
inline int query(int x){
int tmp=0;
for(;x>=1;x-=x&(-x))tmp+=sum[x];
return tmp;
}
int fac[maxn],ans[maxn];
int qwq=0,pt[maxn];
int main(){
int x,y,lca;
//DEBUG
read(n);
fa[1]=0;
f[1][0]=1;
for(ri i=2;i<=n;i++){
f[i][0]=i;
read(fa[i]);
add_edge(i,fa[i]),add_edge(fa[i],i);
}
dep[1]=0;
dfs_1(1);
dfs_2(1,1);
read(m);
for(ri i=1;i<=m;i++){
read(x),read(y);
if(dfn[x]>dfn[y])std::swap(x,y);
lca=get_lca(x,y);
if(!f[x][0]||dep[f[x][0]]>dep[lca])f[x][0]=lca;
if(!f[y][0]||dep[f[y][0]]>dep[lca])f[y][0]=lca;
seg[++qwq]=(Seg){dfn[x],dfn[y],0,1};
}
pre_dfs(1);
fac[0]=1;
for(ri i=1;i<=n;i++)if(f[i][0]==i){
f[i][0]=0;//註意這裏要置為不可行
}
for(ri k=1;k<=21;k++){
fac[k]=(fac[k-1]<<1);
for(ri i=1;i<=n;i++)f[i][k]=f[f[i][k-1]][k-1];
}
//for(ri k=0;k<=2;k++)for(ri i=1;i<=n;i++)printf("%d %d %d\n",i,k,f[i][k]);
int po,qo;
read(q);
for(ri o=1;o<=q;o++){
read(x),read(y);
if(dfn[x]>dfn[y])std::swap(x,y);
lca=get_lca(x,y);
po=x,qo=y;
for(ri i=21;i>=0;i--)
if(dep[f[po][i]]>dep[lca]){
po=f[po][i];
ans[o]+=fac[i];
}
for(ri i=21;i>=0;i--)
if(dep[f[qo][i]]>dep[lca]){
qo=f[qo][i];
ans[o]+=fac[i];
}
if((!f[po][0]&&po!=lca)||(!f[qo][0]&&qo!=lca)){//註意!!
ans[o]=-1;
}
else {
if(po==lca||qo==lca){
ans[o]++;
//printf("--%d %d--\n",o,ans[o]);
}
else {
ans[o]+=2;
//printf("--%d %d--\n",o,ans[o]);
seg[++qwq]=(Seg){dfn[po]-1,dfn[qo]-1,o,1};//二維前綴和
seg[++qwq]=(Seg){ed[po],ed[qo],o,1};
seg[++qwq]=(Seg){dfn[po]-1,ed[qo],o,-1};
seg[++qwq]=(Seg){ed[po],dfn[qo]-1,o,-1};
}
}
}
std::sort(seg+1,seg+1+qwq);
for(ri i=1;i<=qwq;i++){
if(!seg[i].id){
update(seg[i].y,seg[i].d);
}
else{
pt[seg[i].id]+=seg[i].d*query(seg[i].y);
}
}
for(ri i=1;i<=q;i++){
if(pt[i])printf("%d\n",ans[i]-1);
else printf("%d\n",ans[i]);
}
return 0;
}
[NOIP2018模擬賽10.20A]掛分報告