来自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;
}

Codeforces Round #483 (Div. 1) 简要题解的更多相关文章

  1. Codeforces Round #557 (Div. 1) 简要题解

    Codeforces Round #557 (Div. 1) 简要题解 codeforces A. Hide and Seek 枚举起始位置\(a\),如果\(a\)未在序列中出现,则对答案有\(2\ ...

  2. Codeforces Round #545 (Div. 1) 简要题解

    这里没有翻译 Codeforces Round #545 (Div. 1) T1 对于每行每列分别离散化,求出大于这个位置的数字的个数即可. # include <bits/stdc++.h&g ...

  3. Codeforces Round #498 (Div. 3) 简要题解

    [比赛链接] https://codeforces.com/contest/1006 [题解] Problem A. Adjacent Replacements        [算法] 将序列中的所有 ...

  4. Codeforces Round #535(div 3) 简要题解

    Problem A. Two distinct points [题解] 显然 , 当l1不等于r2时 , (l1 , r2)是一组解 否则 , (l1 , l2)是一组合法的解 时间复杂度 : O(1 ...

  5. [题解][Codeforces]Codeforces Round #602 (Div. 1) 简要题解

    orz djq_cpp lgm A 题意 给定一个分别含有 \(\frac n2\) 个左括号和右括号的括号序列 每次可以将序列的一个区间翻转 求一个不超过 \(n\) 次的操作方案,使得操作完之后的 ...

  6. Codeforces Round #398 (div.2)简要题解

    这场cf时间特别好,周六下午,于是就打了打(谁叫我永远1800上不去div1) 比以前div2的题目更均衡了,没有太简单和太难的...好像B题难度高了很多,然后卡了很多人. 然后我最后做了四题,E题感 ...

  7. Codeforces Round #588 (Div. 1) 简要题解

    1. 1229A Marcin and Training Camp 大意: 给定$n$个对$(a_i,b_i)$, 要求选出一个集合, 使得不存在一个元素好于集合中其他所有元素. 若$a_i$的二进制 ...

  8. Codeforces Round #576 (Div. 1) 简要题解 (CDEF)

    1198 C Matching vs Independent Set 大意: 给定$3n$个点的无向图, 求构造$n$条边的匹配, 或$n$个点的独立集. 假设已经构造出$x$条边的匹配, 那么剩余$ ...

  9. # Codeforces Round #529(Div.3)个人题解

    Codeforces Round #529(Div.3)个人题解 前言: 闲来无事补了前天的cf,想着最近刷题有点点怠惰,就直接一场cf一场cf的刷算了,以后的题解也都会以每场的形式写出来 A. Re ...

随机推荐

  1. 异常--try..catch

    class Program { static void Main(string[] args) { try { object obj = null; int N = (int)obj; } catch ...

  2. Spring事务管理Transaction

    Spring提供了许多内置事务管理器实现: DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,数据源事务管理器, ...

  3. 【bzoj1692】[Usaco2007 Dec]队列变换 贪心+后缀数组

    题目描述 FJ打算带他的N(1 <= N <= 30,000)头奶牛去参加一年一度的“全美农场主大奖赛”.在这场比赛中,每个参赛者都必须让他的奶牛排成一列,然后领她们从裁判席前依次走过. ...

  4. 【刷题】洛谷 P2709 小B的询问

    题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重 ...

  5. [洛谷P4999]烦人的数学作业

    题目大意:定义$f(x)$表示$x$每一个数位(十进制)的数之和,求$\sum\limits_{i=l}^rf(i)$,多组询问. 题解:数位$DP$,可以求出每个数字的出现个数,再乘上每个数字的大小 ...

  6. Spark2.1.0之初识Spark

    随着近十年互联网的迅猛发展,越来越多的人融入了互联网——利用搜索引擎查询词条或问题:社交圈子从现实搬到了Facebook.Twitter.微信等社交平台上:女孩子们现在少了逛街,多了在各大电商平台上的 ...

  7. BZOJ2434:[NOI2011]阿狸的打字机——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=2434 https://www.luogu.org/problemnew/show/P2414 打字 ...

  8. [Leetcode] maximun subarray 最大子数组

    Find the contiguous subarray within an array (containing at least one number) which has the largest ...

  9. CodeForces - 158B.Taxi (贪心)

    CodeForces - 158B.Taxi (贪心) 题意分析 首先对1234的个数分别统计,4人组的直接加上即可.然后让1和3成对处理,只有2种情况,第一种是1多,就让剩下的1和2组队处理,另外一 ...

  10. Ark组件[转]

    Ark组件简介 Ark组件是基于.NET 4.0框架开发的基础组件,封装了一些常用的功能方法,并提供了若干程序开发的基础框架. HttpSession简介 HttpSession是Ark组件中负责HT ...