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;
}
posted @ 2018-05-17 09:00  FallDream  阅读(514)  评论(0编辑  收藏  举报