比赛链接
https://codeforces.com/contest/1036
A. Function Height
题目大意
n n 个点,次操作,每次可以使一个点+1,求最终 n n 个点的最大值。
题解
答案就是
B. Diagonal Walking v.2
题目大意
q q 次询问,每次要从走到 (n,m) ( n , m ) ,一个点可以走多次(包括起点和终点),一个点可以走一步到达周围的8个方向,如果是斜着走的一步就叫做“好的移动”,求 (0,0) ( 0 , 0 ) 恰好走 k k 步能不能到达,如果不能输出-1,否则输出最多可以走多少次“好的移动”。
1≤q≤104,1≤n,m,k≤1018 1 ≤ q ≤ 10 4 , 1 ≤ n , m , k ≤ 10 18
题解
-1的情况很好判断,就是max(n,m)>k
。
否则,如果 n+m n + m 与 k k 不同奇偶,必须移动不好的一步来达到同奇偶,剩下的可以全部走斜的。
如果不能恰好走到,也就是与 k k 不同奇偶,那么必须做两次不好的移动使得恰好能走到。
C. Classy Numbers
题目大意
T T 次询问,求区间中有多少个非零数位 ≤3 ≤ 3 的数字。
1≤T≤104,1≤Li,Ri≤1018 1 ≤ T ≤ 10 4 , 1 ≤ L i , R i ≤ 10 18
题解
(伪)数位DP。对于区间 [1,R] [ 1 , R ] ,拆成 [1,a×10x−1],[a×10x [ 1 , a × 10 x − 1 ] , [ a × 10 x , (10a+b)×10x−1−1],[(10a+b)×10x−1 ( 10 a + b ) × 10 x − 1 − 1 ] , [ ( 10 a + b ) × 10 x − 1 , (100a+10b+c)×10x−2−1],⋯ ( 100 a + 10 b + c ) × 10 x − 2 − 1 ] , ⋯ ,每段分别统计答案,每段的答案是很好统计的。
D. Vasya and Arrays
题目大意
有一种操作,将序列的任意一段用它们的和代替。现有两个序列 A,B A , B ,可以执行任意次操作,问两个序列最终能否相同,若能,输出两个序列相同时最长的长度,不能输出-1。
1≤|A|,|B|≤3×105,1≤Ai,Bi≤109 1 ≤ | A | , | B | ≤ 3 × 10 5 , 1 ≤ A i , B i ≤ 10 9
题解
如果两个序列的和不同输出-1。
用两个值维护两个序列的前缀和,一个值大了就往另一个值加数,如果两个值相等答案+1。
E. Covered Points
题目大意
n n 条线段,每条线段从到 (ci,di) ( c i , d i ) ,求至少被一条线段覆盖的整点个数。保证线段两两不在一条直线上。
1≤n≤103,−106≤ai,bi,ci,di≤106 1 ≤ n ≤ 10 3 , − 10 6 ≤ a i , b i , c i , d i ≤ 10 6
题解
如果线段没有交点,那么每条线段对答案的贡献就是 gcd(|ci−ai|,|di−bi|) gcd ( | c i − a i | , | d i − b i | ) 。
如果没有3条线段共用一个交点,那么枚举每条线段的交点,如果是整点答案-1。
如果没有上面这些限制,我们可以开一个set,存每条线段与前面线段的交点,最后答案-set的大小。
F. Relatively Prime Powers
题目大意
一个数可以表示成 ak11⋅ak22⋯aknn a 1 k 1 ⋅ a 2 k 2 ⋯ a n k n ,如果 gcd(k1,k2,⋯,kn)=1 gcd ( k 1 , k 2 , ⋯ , k n ) = 1 ,就说这个数是好数。 T T 组询问,求范围内的好数个数。
1≤T≤105,1≤Mi≤1018 1 ≤ T ≤ 10 5 , 1 ≤ M i ≤ 10 18
题解
显然,
[2,N]
[
2
,
N
]
范围内好数个数为
但是如果直接这样算是 O(Tlog2N) O ( T log 2 N ) 。
考虑离线算法,按照询问降序排列。
对于每个询问,当 i=2 i = 2 时, n‾√ n 很容易在 O(logN) O ( log N ) 时间内得到,而 i≥3 i ≥ 3 时, ∑∞i=3⌊N1/i−1⌋ ∑ i = 3 ∞ ⌊ N 1 / i − 1 ⌋ 的值不超过 1.1×106 1.1 × 10 6 ,开一个数组记录 N1/i N 1 / i ,可以对于每个询问依次减到实际值。 μ(i) μ ( i ) 可以预处理 O(logN) O ( log N ) 。
总的时间复杂度大概是 O(TlogN+1.1×106) O ( T log N + 1.1 × 10 6 ) 。
G. Sources and Sinks
题目大意
将没有入度的点称为源,将没有出度的点称为汇,一个点可以既是源又是汇。给一个 n n 个点条边的图,保证源和汇的个数相同且都 ≤20 ≤ 20 。现有一个算法:
- 找一个源和一个汇,可以相同。
- 连一条边从汇到源,如果这个图还有源和汇,进入1。
对于这个图,如果在1过程中,无论如何选取源和汇,最后得到的图都是强连通的,输出YES
,否则输出NO
。
题解
设源和汇的个数都为 C C 。
如果一个源集合能够到达的所有汇为
T
T
,对于任意,若
|S|≠0,|S|≠C,|S|≥|T|
|
S
|
≠
0
,
|
S
|
≠
C
,
|
S
|
≥
|
T
|
,那么应该输出NO
,因为如果
T
T
中每个点都与中的点连边,那么
S
S
中的点就不能到达非中的汇点。
现在我们用数学归纳法证明如果对于每个
S
S
,都有或
|S|<|T|
|
S
|
<
|
T
|
,那么应该输出YES
。
任取一个点 s s 。
- 一定能走到 s s (显然)。
- 若能走到的汇的集合为
T
T
,如果,那么证明了强连通;
- 否则,记 T T 能走到的源为, S S 能走到的汇为,由于 |T|=|S|<|T′| | T | = | S | < | T ′ | ,因此每一次 |T| | T | 的值都在变大,最终变成 |T|=C | T | = C 。
因此只要 O(C2C) O ( C 2 C ) 的检查源的集合就可以判断了。
代码
A. Function Height
#include <cstdio> long long a,b; int main() { scanf("%I64d%I64d",&a,&b); printf("%I64d\n",(b+a-1)/a); return 0; }
B. Diagonal Walking v.2
#include <cstdio> #include <algorithm> int q; long long n,m,k; int main() { scanf("%d",&q); while(q--) { scanf("%I64d%I64d%I64d",&n,&m,&k); if(n<m) { std::swap(n,m); } if(k<n) { puts("-1"); continue; } if((n-m)&1) { --k; } else if((k-n)&1) { k-=2; } printf("%I64d\n",k); } return 0; }
C. Classy Numbers
#include <cstdio> const int pow[]={1,9,81,729}; int C(int a,int b) { if(b>a) { return 0; } int res=1; for(int i=1; i<=b; ++i) { res=res*(a-i+1)/i; } return res; } int getval(long long x) { int have=3,ans=0; long long bit=1; for(int i=1; i<=18; ++i) { bit*=10; } for(int i=18; i>=0; --i) { int v=x/bit; x%=bit; bit/=10; if(v==0) { continue; } for(int j=0; j<have; ++j) { ans+=v*C(i,j)*pow[j]; } ans+=C(i,have)*pow[have]; --have; if(have<0) { break; } } if(have>=0) { ++ans; } return ans; } int t; long long a,b; int main() { scanf("%d",&t); while(t--) { scanf("%I64d%I64d",&a,&b); printf("%d\n",getval(b)-getval(a-1)); } return 0; }
D. Vasya and Arrays
#include <cstdio> int read() { int x=0,f=1; char ch=getchar(); while((ch<'0')||(ch>'9')) { if(ch=='-') { f=-f; } ch=getchar(); } while((ch>='0')&&(ch<='9')) { x=x*10+ch-'0'; ch=getchar(); } return x*f; } const int maxn=300000; int n,m,a[maxn+10],b[maxn+10],ans; long long suma,sumb; int main() { n=read(); for(int i=1; i<=n; ++i) { a[i]=read(); suma+=a[i]; } m=read(); for(int i=1; i<=m; ++i) { b[i]=read(); sumb+=b[i]; } if(suma!=sumb) { puts("-1"); return 0; } suma=sumb=0; int l=1,r=1; while((l<=n)||(r<=m)) { if(suma<=sumb) { suma+=a[l++]; } else { sumb+=b[r++]; } if(suma==sumb) { ++ans; } } printf("%d\n",ans); return 0; }
E. Covered Points
#include <set> #include <cmath> #include <cstdio> #include <iostream> #include <algorithm> int read() { int x=0,f=1; char ch=getchar(); while((ch<'0')||(ch>'9')) { if(ch=='-') { f=-f; } ch=getchar(); } while((ch>='0')&&(ch<='9')) { x=x*10+ch-'0'; ch=getchar(); } return x*f; } typedef std::pair<int,int> pii; const int maxn=1000; const double eps=0.00000001; struct point { double x,y; point(double _x=0,double _y=0):x(_x),y(_y){} }; struct seg { point l,r; seg(point _l=0,point _r=0):l(_l),r(_r){} }; int n,ans; seg s[maxn+10]; std::set<pii> t; point getcp(seg a,seg b) { double x1=a.l.x-a.r.x,x2=b.l.x-b.r.x,y1=a.l.y-a.r.y,y2=b.l.y-b.r.y; double bot=y1*x2-x1*y2; point ans(((b.r.y-a.r.y)*(x1*x2)+a.r.x*(y1*x2)-b.r.x*(x1*y2))/bot, ((a.r.x-b.r.x)*(y1*y2)+b.r.y*(y1*x2)-a.r.y*(x1*y2))/bot); return ans; } int gcd(int a,int b) { return b?gcd(b,a%b):a; } int tr(double x) { if(x>=0) { return (int)(x+0.5); } else { return (int)(x-0.5); } } int main() { n=read(); for(int i=1; i<=n; ++i) { int lx=read(),ly=read(),rx=read(),ry=read(); s[i]=seg(point(lx,ly),point(rx,ry)); int dx=abs(rx-lx),dy=abs(ry-ly); ans+=gcd(dx,dy)+1; t.clear(); for(int j=1; j<i; ++j) { point cp=getcp(s[i],s[j]); int cpx=tr(cp.x),cpy=tr(cp.y); if((std::abs(cpx-cp.x)<eps)&&(std::abs(cpy-cp.y)<eps) &&(cpx>=std::min(lx,rx))&&(cpx>=std::min(s[j].l.x,s[j].r.x)) &&(cpx<=std::max(lx,rx))&&(cpx<=std::max(s[j].l.x,s[j].r.x)) &&(cpy>=std::min(ly,ry))&&(cpy>=std::min(s[j].l.y,s[j].r.y)) &&(cpy<=std::max(ly,ry))&&(cpy<=std::max(s[j].l.y,s[j].r.y))) { t.insert(std::make_pair(cpx,cpy)); } } ans-=t.size(); } printf("%d\n",ans); return 0; }
F. Relatively Prime Powers
#include <cmath> #include <cstdio> #include <algorithm> const int maxk=65; const int maxn=100000; const double eps=0.000001; const long long maxv=1000000000000000000ll; struct query { int id; long long val; bool operator <(const query &other) const { return val>other.val; } }; int p[maxk+10],prime[maxk+10],mu[maxk+10],cnt,end[maxk+10],t; long long ans[maxn+10]; query q[maxn+10]; int getmu() { p[1]=1; mu[1]=1; for(int i=1; i<=maxk; ++i) { if(!p[i]) { prime[++cnt]=i; mu[i]=-1; } for(int j=1; (j<=cnt)&&(i*prime[j]<=maxk); ++j) { p[i*prime[j]]=1; if(i%prime[j]==0) { mu[i*prime[j]]=0; break; } mu[i*prime[j]]=-mu[i]; } } return 0; } long long spow(long long x,int b) { long long res=1; while(b) { if(b&1) { res=res*x; } x=x*x; b>>=1; } return res; } long long check(long long x) { end[2]=sqrt(x); for(int i=3; i<=maxk; ++i) { while(spow(end[i],i)>x) { --end[i]; } } long long res=x-1; for(int i=2; i<=maxk; ++i) { res+=mu[i]*(end[i]-1); } return res; } int main() { getmu(); for(int i=3; i<=maxk; ++i) { end[i]=(long long)(pow(maxv,1.0/i)+eps); } scanf("%d",&t); for(int i=1; i<=t; ++i) { scanf("%I64d",&q[i].val); q[i].id=i; } std::sort(q+1,q+t+1); for(int i=1; i<=t; ++i) { ans[q[i].id]=check(q[i].val); } for(int i=1; i<=t; ++i) { printf("%I64d\n",ans[i]); } return 0; }
G. Sources and Sinks
#include <cstdio> int read() { int x=0,f=1; char ch=getchar(); while((ch<'0')||(ch>'9')) { if(ch=='-') { f=-f; } ch=getchar(); } while((ch>='0')&&(ch<='9')) { x=x*10+ch-'0'; ch=getchar(); } return x*f; } const int maxn=1000000; const int maxk=20; int source[maxk+3],id[maxn+3],n,m,cnts,cntt; int ru[maxn+10],chu[maxn+10],to[maxk+3]; int pre[maxn+10],now[maxn+10],son[maxn+10],tot; int ins(int a,int b) { pre[++tot]=now[a]; now[a]=tot; son[tot]=b; return 0; } int search(int u,int st) { if(id[u]) { to[st]|=1<<(id[u]-1); } int j=now[u]; while(j) { int v=son[j]; search(v,st); j=pre[j]; } return 0; } int main() { n=read(); m=read(); for(int i=1; i<=m; ++i) { int a=read(),b=read(); ins(a,b); ++chu[a]; ++ru[b]; } for(int i=1; i<=n; ++i) { if(!ru[i]) { source[++cnts]=i; } if(!chu[i]) { id[i]=++cntt; } } for(int i=1; i<=cnts; ++i) { search(source[i],i); } int flag=0; for(int sta=1; sta<(1<<cnts)-1; ++sta) { int ttot=0,scnt=0,cnt=0; for(int i=1; i<=cnts; ++i) { if(sta&(1<<(i-1))) { ++scnt; ttot|=to[i]; } } for(int i=1; i<=cnts; ++i) { if(ttot&(1<<(i-1))) { ++cnt; } } if(cnt<=scnt) { flag=1; break; } } puts(flag?"NO":"YES"); return 0; }