Codeforces Round #483 (Div. 1) 简要题解
来自FallDream的博客,未经允许,请勿转载,谢谢。
为了证明一下我又来更新了,写一篇简要的题解吧。
这场比赛好像有点神奇,E题莫名是道原题,导致有很多选手直接过掉了(Claris 表演24s过题)。然而D题比E题要难一些,分还少。
A. Finite or not?
先把\(\frac{p}{q}\)约成最简分数,然后就是要判断是否\(q\)的所有质因数都是\(b\)的质因数。
每次取\(g=gcd(b,q)\),并尽可能的让\(q\)除\(g\),最后判断\(q\)是否是1即可。
还有一种思路,就是求\(b^{64} mod q\),判断这个值是否为0,但是乘法是大整数之间进行的,所以稍微有点复杂。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
int main()
{
for(int T=read();T;--T)
{
ll p=read(),q=read(),b=read();
ll g=gcd(p,q),c;p/=g;q/=g;
while((g=gcd(q,b))>1)
do q/=g; while(q%g==0);
puts(q>1?"Infinite":"Finite");
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
inline ll mul(ll x,ll mod){return ((x*x-(ll)((long double)x*x/mod)*mod)%mod+mod)%mod;}
int main()
{
for(int T=read();T;--T)
{
ll p=read(),q=read(),b=read();
q/=gcd(p,q);b%=q;
for(int i=1;i<=6&&b;++i) b=mul(b,q);
puts(b?"Infinite":"Finite");
}
return 0;
}
B. XOR-pyramid
没什么好说的,令f[i][j]
表示区间\([i,j]\)得到的结果,\(f[i][j]=f[i][j-1] xor f[i+1][j]\)
然后求一个区间最大值即可。
#include<bits/stdc++.h>
#define MN 5000
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int f[MN+5][MN+5],n,Q,mx[MN+5][MN+5];
int main()
{
n=read();
for(int i=1;i<=n;++i) f[i][i]=mx[i][i]=read();
for(int i=n;i;--i) for(int j=i+1;j<=n;++j)
f[i][j]=f[i][j-1]^f[i+1][j],mx[i][j]=max(f[i][j],max(mx[i+1][j],mx[i][j-1]));
for(int Q=read(),l,r;Q--;) l=read(),r=read(),printf("%d\n",mx[l][r]);
return 0;
}
C. Elevator
先搜出所有电梯上人的状态,总共只有不到720种,然后再记一下前几个人已经进了电梯,现在在几楼,状态树不超过720*2000*9,转移数量\(O(1)\),bfs即可。
#include<bits/stdc++.h>
#define MN 2000
#define MX 720
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int ans=1e9,n,a[MN+5],b[MN+5],cnt,num[MX+5],A[6],c[MX+5][5];
int id[10][10][10][10],f[15000005],q[15000005],top;
void dfs(int x,int v)
{
++cnt;num[cnt]=x-1;id[A[1]][A[2]][A[3]][A[4]]=cnt;
for(int j=1;j<x;++j) c[cnt][j]=A[j];
if(x>4)return;
for(int i=v;i<=9;++i) A[x]=i,dfs(x+1,i);
A[x]=0;
}
inline int ID(int x,int y,int z){return x*MX*10+y*10+z;}
void Try(int x,int y,int z,int v)
{
int d=ID(x,y,z);
if(v<f[d]) f[q[++top]=d]=v;
}
int main()
{
dfs(1,1);memset(f,40,sizeof(f));
n=read();
for(int i=1;i<=n;++i) a[i]=read(),b[i]=read();
f[q[top=1]=ID(0,id[0][0][0][0],1)]=0;
for(int i=1;i<=top;++i)
{
int x=q[i]/MX/10,y=(q[i]/10)%MX,z=q[i]%10,v=f[q[i]];
if(z<9) Try(x,y,z+1,v+1);
if(z>1) Try(x,y,z-1,v+1);
int nn=0;
for(int j=1;j<=num[y];++j)
if(c[y][j]!=z) A[++nn]=c[y][j];
for(int j=nn+1;j<=4;++j) A[j]=0;
Try(x,id[A[1]][A[2]][A[3]][A[4]],z,v+(num[y]-nn));
if(x<n&&a[x+1]==z&&num[y]<4)
{
int nn=0,k=1;
for(;k<=num[y];++k)
if(c[y][k]<=b[x+1]) A[++nn]=c[y][k];
else break;
A[++nn]=b[x+1];
for(;k<=num[y];++k) A[++nn]=c[y][k];
while(nn<4) A[++nn]=0;
Try(x+1,id[A[1]][A[2]][A[3]][A[4]],z,v+1);
}
}
for(int i=1;i<=9;++i) ans=min(ans,f[ID(n,id[0][0][0][0],i)]);
cout<<ans;
return 0;
}
D. Arkady and Rectangles
这个题有点意思。。。
首先讲讲我自己想的一个做法吧。
考虑对\(x\)建线段树,在每个节点上维护y的颜色段,对于每个矩形,将它插入到线段树上对应节点上。
然后考虑每一个颜色段是否能被看到,必须满足线段树上它的父亲中没有将它完全盖掉,并且两个儿子也不都盖掉它。类似线段树分治的思想,可以按照分治层数可持久化一下,维护一棵线段树支持区间取max,区间最小值,这样就可以处理父亲的影响了。对于儿子的,考虑现在正在计算点\(x\)上的某一个颜色段是否满足,那么枚举左右儿子中每一段与这个段相交的段,假设左右儿子中的最小值满足条件,那么儿子也不会产生影响。
处理好影响之后,可以将这个点的颜色段和儿子的段合并,记下每段最小值。
这个做法是\(O(nlog^{2}n)\)的,空间可以利用线段树分治时版本很少的性质做到\(O(nlogn)\),听起来常数就很大,实际上更大,加一些优化可以卡过去。
然后有比较简单的做法,考虑对横坐标扫描线,维护一棵关于y的线段树。对于每个矩形,依旧插入到对应的y坐标节点,然后我们求出每个点代表的区间能看到的矩形的最大值,这个信息不难用set维护。
每次操作后,我们考虑线段树根节点能看到什么矩形,暴力记下它之后,将它标记为已经看到。对于已经看到的矩形,除了在合并区间信息时稍有不同以外,没有太大区别,每个矩形只会被看到1次。
这个做法是时空级别一样但是显然会更小,常数也小得多,可以轻松跑过去。
不过这个做法cf上好像挺多人写的,我就不写了(跑
贴一个我的sb做法。
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#include<bits/stdc++.h>
#define MN 100000
#define G() st[x].lower_bound((Li){L,0,0})
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct Rec{int x1,y1,x2,y2;}s[MN+5];
int n,Lx[MN*2+5],Ly[MN*2+5],numx,numy,ans,vis[MN+5],B,C,cnt,rt[55];
struct Li{int l,r,v;}a[MN+5],b[MN+5],c[MN+5];
struct Tree{int l,r,mx,x;short d;}T[4000005];
struct cmp{bool operator()(const Li&a,const Li&b){return a.l==b.l?(a.r==b.r?a.v<b.v:a.r<b.r):a.l<b.l;}};
set<Li,cmp> st[524295];set<Li,cmp>::iterator it;
void Ins(int x,int lt,int rt,int l,int r,int L,int R,int now)
{
if(l==lt&&r==rt)
{
it=G();
if(it!=st[x].begin())
{
--it;Li y=*it;
if(it->r>R)
{
st[x].erase(it);
st[x].insert((Li){y.l,L-1,y.v});
st[x].insert((Li){R+1,y.r,y.v});
}
else if(it->r>=L)
{
st[x].erase(it);
st[x].insert((Li){y.l,L-1,y.v});
}
}
for(it=G();it->l<=R;it=G())
{
Li y=*it;st[x].erase(it);
if(y.r>R) st[x].insert((Li){R+1,y.r,y.v});
}
st[x].insert((Li){L,R,now});
return;
}
int mid=lt+rt>>1;
if(r<=mid) Ins(x<<1,lt,mid,l,r,L,R,now);
else if(l>mid) Ins(x<<1|1,mid+1,rt,l,r,L,R,now);
else Ins(x<<1,lt,mid,l,mid,L,R,now),Ins(x<<1|1,mid+1,rt,mid+1,r,L,R,now);
}
void Build(int x,int l,int r)
{
if(st[x].insert((Li){1,numy,0}),l==r) return;
int mid=l+r>>1;
Build(x<<1,l,mid);Build(x<<1|1,mid+1,r);
}
int now_dep;
inline int newnode(int x){return T[x].d==now_dep?x:(T[++cnt]=T[x],T[cnt].d=now_dep,cnt);}
inline void update(int x){T[x].mx=max(T[x].x,min(T[T[x].l].mx,T[T[x].r].mx));}
void Modify(int x,int lt,int rt,int l,int r,int v)
{
if(lt==l&&rt==r){T[x].x=max(T[x].x,v);T[x].mx=max(T[x].mx,v);return;}
int mid=lt+rt>>1;
if(r<=mid) Modify(T[x].l=newnode(T[x].l),lt,mid,l,r,v);
else if(l>mid) Modify(T[x].r=newnode(T[x].r),mid+1,rt,l,r,v);
else Modify(T[x].l=newnode(T[x].l),lt,mid,l,mid,v),
Modify(T[x].r=newnode(T[x].r),mid+1,rt,mid+1,r,v);
update(x);
}
int Query(int x,int lt,int rt,int l,int r)
{
if(!x) return 0;
if(lt==l&&rt==r) return T[x].mx;
int mid=lt+rt>>1;
if(r<=mid) return max(Query(T[x].l,lt,mid,l,r),T[x].x);
else if(l>mid) return max(Query(T[x].r,mid+1,rt,l,r),T[x].x);
else return max(T[x].x,min(Query(T[x].l,lt,mid,l,mid),Query(T[x].r,mid+1,rt,mid+1,r)));
}
void Solve(int x,int l,int r,int dep)
{
if(l==r)
{
for(it=st[x].begin();it!=st[x].end();++it)
if(Query(rt[dep-1],1,numy,it->l,it->r)<=it->v)
if(!vis[it->v]) vis[it->v]=1,++ans;
return;
}
now_dep=dep;
int mid=l+r>>1,A=0,L=x<<1,R=L|1,precnt=cnt;
rt[dep]=newnode(rt[dep-1]);
for(it=st[x].begin();it!=st[x].end();++it) Modify(rt[dep],1,numy,it->l,it->r,it->v);
Solve(L,l,mid,dep+1);Solve(R,mid+1,r,dep+1);B=C=0;
for(it=st[x].begin();it!=st[x].end();++it) a[++A]=*it;
st[x].clear();
for(it=st[L].begin();it!=st[L].end();++it) b[++B]=*it;st[L].clear();
for(it=st[R].begin();it!=st[R].end();++it) c[++C]=*it;st[R].clear();
for(int i=1,j=1,k=1;i<=A;++i)
{
int ok=0;
while(j<=B&&b[j].r<a[i].l) ++j;
while(k<=C&&c[k].r<a[i].l) ++k;
while(j>1&&b[j-1].r>=a[i].l) --j;
while(k>1&&c[k-1].r>=a[i].l) --k;
for(int last=a[i].l-1;!ok&&last<a[i].r;)
{
int v=min(b[j].v,c[k].v),rr=min(b[j].r,c[k].r);
if(v<=a[i].v&&Query(rt[dep],1,numy,last+1,min(rr,a[i].r))<=a[i].v) ok=1;
last=rr;
if(b[j].r==rr) ++j;
if(c[k].r==rr) ++k;
}
if(ok&&!vis[a[i].v]) vis[a[i].v]=1,++ans;
}
if(x==1) return;
for(int i=1,j=1,k=1,last=0;i<=A;)
{
int v=max(a[i].v,min(b[j].v,c[k].v)),rr=min(a[i].r,min(b[j].r,c[k].r));
st[x].insert((Li){last+1,rr,v});last=rr;
if(a[i].r==rr) ++i;
if(b[j].r==rr) ++j;
if(c[k].r==rr) ++k;
}
cnt=precnt;
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
{
s[i].x1=read();s[i].y1=read();
s[i].x2=read();s[i].y2=read();
Lx[++numx]=s[i].x1;Lx[++numx]=s[i].x2;
Ly[++numy]=s[i].y1;Ly[++numy]=s[i].y2;
}
sort(Lx+1,Lx+numx+1);numx=unique(Lx+1,Lx+numx+1)-Lx-1;
sort(Ly+1,Ly+numy+1);numy=unique(Ly+1,Ly+numy+1)-Ly-1;
Build(1,1,numx);
for(int i=1;i<=n;++i)
{
s[i].x1=lower_bound(Lx+1,Lx+numx+1,s[i].x1)-Lx;
s[i].x2=lower_bound(Lx+1,Lx+numx+1,s[i].x2)-Lx-1;
s[i].y1=lower_bound(Ly+1,Ly+numy+1,s[i].y1)-Ly;
s[i].y2=lower_bound(Ly+1,Ly+numy+1,s[i].y2)-Ly-1;
Ins(1,1,numx,s[i].x1,s[i].x2,s[i].y1,s[i].y2,i);
}
Solve(1,1,numx,1);
printf("%d\n",ans);
return 0;
}
E. NN country
原题是bzoj2167,但是数据范围稍有加大。
这道题以前还被拿出来联考了,当时就讨论过了一个log的做法。
首先考虑最终的走法,肯定是先从两个点尽可能往lca上面跳,然后坐巴士绕过lca或者做两次,并在lca转车。
先简单求出每个点走一步能走到的最小深度,然后通过倍增实现尽可能跳的这一步。
接下来只需要判断有没有同时经过这两个点的线路了,这就是一个简单的二维数点问题。
#include<bits/stdc++.h>
#define MN 200000
#define MD 18
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
vector<int> v[MN+5],V[MN+5];
int n,m,Q,fa[MD+1][MN+5],f[MD+1][MN+5],dep[MN+5],rt[MN+5],dfn[MN+5],dn,p[MN+5],nr[MN+5],cnt;
struct Tree{int l,r,x;}T[12000005];
inline int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int k=dep[x]-dep[y],j=0;k;k>>=1,++j)
if(k&1) x=fa[j][x];
if(x==y)return x;
for(int i=MD;~i;--i) if(fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
return fa[0][x];
}
void dfs(int x)
{
dfn[x]=++dn;p[dn]=x;
for(int i=0;i<v[x].size();++i) dfs(v[x][i]);
nr[x]=dn;
}
int Query(int x,int l,int r,int lt=1,int rt=n)
{
if(!x||(l==lt&&r==rt))return T[x].x;
int mid=lt+rt>>1;
if(r<=mid) return Query(T[x].l,l,r,lt,mid);
else if(l>mid) return Query(T[x].r,l,r,mid+1,rt);
else return Query(T[x].l,l,mid,lt,mid)+Query(T[x].r,mid+1,r,mid+1,rt);
}
inline int newnode(int x){T[++cnt]=T[x];return cnt;}
void Modify(int x,int l,int r,int k)
{
if(++T[x].x,l==r) return;
int mid=l+r>>1;
if(k<=mid) Modify(T[x].l=newnode(T[x].l),l,mid,k);
else Modify(T[x].r=newnode(T[x].r),mid+1,r,k);
}
inline int Min(int x,int y){return dep[x]<dep[y]?x:y;}
int main()
{
n=read();
for(int i=2;i<=n;++i) fa[0][i]=read(),dep[i]=dep[fa[0][i]]+1,v[fa[0][i]].push_back(i);
for(int i=1;i<=n;++i) f[0][i]=i;dfs(1);
for(int i=1;i<=MD;++i) for(int j=1;j<=n;++j) fa[i][j]=fa[i-1][fa[i-1][j]];
m=read();
for(int i=1;i<=m;++i)
{
int x=read(),y=read(),z=lca(x,y);
f[0][x]=Min(f[0][x],z);
f[0][y]=Min(f[0][y],z);
if(dfn[x]>dfn[y]) swap(x,y);
V[y].push_back(x);
}
for(int i=1;i<=n;++i)
{
rt[i]=newnode(rt[i-1]);
for(int j=0;j<V[p[i]].size();++j)
Modify(rt[i],1,n,dfn[V[p[i]][j]]);
}
for(int i=n;i;--i) f[0][fa[0][i]]=Min(f[0][fa[0][i]],f[0][i]);
for(int i=1;i<=MD;++i) for(int j=1;j<=n;++j) f[i][j]=f[i-1][f[i-1][j]];
Q=read();
for(int i=1;i<=Q;++i)
{
int x=read(),y=read(),z=lca(x,y),ans=0;
for(int j=MD;~j;--j)
{
if(dep[f[j][x]]>dep[z]) ans+=1<<j,x=f[j][x];
if(dep[f[j][y]]>dep[z]) ans+=1<<j,y=f[j][y];
}
if(ans>n) {puts("-1");continue;}
if(x==z||y==z) {printf("%d\n",ans+1);continue;}
if(dfn[x]>dfn[y]) swap(x,y);
printf("%d\n",ans+2-bool(Query(rt[nr[y]],dfn[x],nr[x])-Query(rt[dfn[y]-1],dfn[x],nr[x])));
}
return 0;
}