Educational Codeforces Round 50
Educational Codeforces Round 50
G. Sources and Sinks
description
有一张\(DAG\),定义source为入度为零的点,sink为出度为零的点。保证source和sink的个数相等且不超过\(20\) 。定义匹配一个source和一个sink为从sink向source连一条有向边。问是否任意一个source与sink之间的完美匹配都可以使原图变成强连通。
\(n\le10^6\)
solution
设source与sink各有\(C\)个,那么只需要保证这\(2C\)个点组成的图强连通就行了。
有一个结论是:对于任意一个source的集合\(S\) ,\(|S|\neq0,|S|\neq C\) ,它能到达的sink集合是\(f(S)\) ,则需要有\(|S|>|f(S)|\) 。
复杂度\(O(C(n+m)+C2^C)\)
code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 1e6+5;
int n,m,cnt[N<<1],to[N],nxt[N],head[N],in[N],val[N],C,vis[N],ans[20];
int dfs(int u){
vis[u]=1;int res=val[u];
for (int e=head[u];e;e=nxt[e])
if (!vis[to[e]]) res|=dfs(to[e]);
return res;
}
int main(){
n=gi();m=gi();
for (int i=1;i<=m;++i){
int u=gi(),v=gi();
to[i]=v;nxt[i]=head[u];head[u]=i;++in[v];
}
for (int i=1;i<=n;++i) if (!head[i]) val[i]=1<<C++;
for (int i=1,j=0;j<C;++i) if (!in[i]) memset(vis,0,sizeof(vis)),ans[j++]=dfs(i);
for (int i=0;i<(1<<C);++i) cnt[i]=cnt[i>>1]+(i&1);
for (int i=1;i<(1<<C)-1;++i){
int res=0;
for (int j=0;j<C;++j) if (i>>j&1) res|=ans[j];
if (cnt[i]>=cnt[res]) return puts("NO"),0;
}
return puts("YES"),0;
}
F. Relatively Prime Powers
description
将一个数质因数分解为\(p_1^{a_1}p_2^{a_2}...p_k^{a_k}\) ,定义其是elegent当且仅当\(\gcd(a_1,a_2...a_k)=1\) 。
\(T\)组询问,每次询问\(2-n\)中elegent的数的个数。
\(T\le10^5,n\le10^{18}\)
solution
首先莫比乌斯随便演一下答案就是\(\sum_{i=1}^{\infty}\mu(i)(\sqrt[i]n-1)\) 。
直接算的话复杂度\(O(T\log^2n)\)(每个质因子二分根号的那个值)可能过不了。
把询问离线,从大到小做,可以省去一个二分。
复杂度\(O(T\log T+T\log n)\)
code
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long
const int N = 1e5+5;
struct node{
ll x;int y;
bool operator < (const node &b) const
{return x>b.x;}
}a[N];
int mu[61]={0,1,-1,-1,0,-1,1,-1,0,0,1,-1,0,-1,1,1,0,-1,0,-1,0,1,1,-1,0,0,1,0,0,-1,-1,-1,0,1,1,1,0,-1,1,1,0,-1,-1,-1,0,0,1,-1,0,0,0,1,0,-1,0,1,0,1,1,-1,0};
ll mx[61]={0,0,0,1000000,31623,3982,1000,373,178,100,67,46,33,25,20,16,13,11,10,9,8,7,6,6,5,5,5,4,4,4,4,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};
int n;ll now[61],ans[N];
ll fastpow(ll a,int b){
ll res=1;
while (b) {if (b&1) res*=a;a*=a;b>>=1;}
return res;
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%I64d",&a[i].x),a[i].y=i;
sort(a+1,a+n+1);
for (int i=3;i<=60;++i) now[i]=fastpow(mx[i],i);
for (int i=1;i<=n;++i){
mx[1]=a[i].x;mx[2]=sqrt(a[i].x);
for (int j=3;j<=60;++j)
while (now[j]>a[i].x) --mx[j],now[j]=fastpow(mx[j],j);
for (int j=1;j<=60;++j) ans[a[i].y]+=1ll*(mx[j]-1)*mu[j];
}
for (int i=1;i<=n;++i) printf("%I64d\n",ans[i]);
return 0;
}
E. Covered Points
description
给你\(n\)条线段,问你有多少个点被这些线段覆盖了。
\(n\le1000,\)坐标范围\(\le10^6\)
solution
先不考虑算重,那么一条直线的贡献是\(\gcd(|x_1-x_2|,|y_1-y_2|)+1\) 。
然后因为两条不重合直线至多一个交点,所以可以直接解出来交点,然后判重即可。
算交点直接联立两个一般式解方程,一般式用两点式求。
复杂度\(O(n^2\log n)\)
code
#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
#define ll long long
const int N = 1005;
struct seg{
int x1,y1,x2,y2;
int cal(){
return __gcd(abs(x2-x1),abs(y2-y1))+1;
}
bool in(int x,int y){
bool fg=1;
if (x1<=x2) fg&=(x1<=x&&x<=x2);
else fg&=(x2<=x&&x<=x1);
if (y1<=y2) fg&=(y1<=y&&y<=y2);
else fg&=(y2<=y&&y<=y1);
return fg;
}
}l[N];
int n,len,ans;pair<int,int>o[N];
struct line{
ll A,B,C;
line(){}
line(seg a){
A=a.y2-a.y1;B=a.x1-a.x2;
C=-A*a.x1-B*a.y1;
}
};
bool cross(int i,int j,int &X,int &Y){
line a(l[i]),b(l[j]);
ll x=a.B*b.C-a.C*b.B,y=-a.A*b.C+a.C*b.A,z=a.A*b.B-a.B*b.A;
if (!z) return false;if (x%z||y%z) return false;
X=x/z;Y=y/z;
if (l[i].in(X,Y)&&l[j].in(X,Y)) return true;return false;
}
int main(){
n=gi();
for (int i=1;i<=n;++i){
l[i]=(seg){gi(),gi(),gi(),gi()};
ans+=l[i].cal();len=0;
for (int j=1,x,y;j<i;++j)
if (cross(i,j,x,y)) o[++len]=make_pair(x,y);
sort(o+1,o+len+1);len=unique(o+1,o+len+1)-o-1;ans-=len;
}
printf("%d\n",ans);return 0;
}
D. Vasya and Arrays
description
给你两个数组\(A,B\) ,你的每次操作是可以把连续一段数合在一起,值为这些数的和。
求把两个数组变成相同后数组的最长长度。
\(n\le3\times10^5,1\le a_i,b_i\le10^9\)
solution
贪心,每次用双指针找到两个数组最短的相等前缀。复杂度线性。
code
#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 3e5+5;
int a[N],b[N];
int main(){
int n=gi(),i,j,ans=0;
for (i=1;i<=n;++i) a[i]=gi();
int m=gi();
for (i=1;i<=m;++i) b[i]=gi();
long long sa=a[1],sb=b[1];
for (i=1,j=1;i<=n&&j<=m;){
if (sa==sb) ++ans,sa=a[++i],sb=b[++j];
else if (sa>sb) sb+=b[++j];
else sa+=a[++i];
}
while (i<n) sa+=a[++i];
while (j<m) sb+=b[++j];
if (sa==sb) printf("%d\n",ans);
else puts("-1");
return 0;
}
C. Classy Numbers
description
定义一个数是classy的当且仅当其在十进制表示下至多有\(3\)个非零位。
\(T\)组询问,每次问\([L_i,R_i]\)之间有多少个classy的数。
\(T\le10^4,L_i,R_i\le10^18\)
solution
可以直接数位dp。
我比较懒,用了另一种方法:拿组合数算一下会发现满足条件的数只有60w+个,所以就爆搜出来再二分就行了。
code
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll o[1000005],L,R;int T,len;
void dfs(int i,int j,ll sum){
if (i==18) {o[++len]=sum;return;}
dfs(i+1,j,sum*10);
if (j<3)
for (int k=1;k<=9;++k)
dfs(i+1,j+1,sum*10+k);
}
int main(){
dfs(0,0,0);o[++len]=1e18;
scanf("%d",&T);while (T--){
scanf("%I64d%I64d",&L,&R);
printf("%d\n",upper_bound(o+1,o+len+1,R)-lower_bound(o+1,o+len+1,L));
}
return 0;
}
B. Diagonal Walking v.2
description
\(q\)组询问,每次从\((0,0)\)点出发走向\((n,m)\),每一步可以走到横纵坐标\(\pm1\)的位置(不能不走,也就是有\(8\)种走法)要求恰好走\(k\)步,求最多可以斜着走多少步。
\(q\le10^4,n,m,k\le10^{18}\)
solution
走法大体上分成两个阶段(假设\(n>m\)):第一阶段横着走到\((n-m,0)\),当然这里的横着走指的是一上一下,即在走了偶数步后结果上还是横着走的;第二阶段斜着走到\((n,m)\)。
步数可能会多,多余的偶数步可以在终点消耗掉,并且都可以算贡献。现在只需要考虑怎么消耗掉一步:
如果要在第一阶段消耗掉一步,必须要\(n-m\)是奇数,这样等效于把起点移到\((1,0)\) ,同时步数减\(1\);
如果要在第二阶段消耗掉一步,必须要\(n-k\)是奇数,这样等效于把起点移到\((1,1)\) ,同时步数减\(2\)。
所以优先在第一阶段消耗掉这一步,这样的话答案就是\(k\)了。
code
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll gi(){
ll x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
int main(){
int q=gi();while (q--){
ll x=gi(),y=gi(),k=gi();if (x<y) swap(x,y);
if ((x&1)^(y&1)) --k,--x;
else if ((x&1)^(k&1)) k-=2,--x,--y;
printf("%I64d\n",k<x?-1:k);
}
return 0;
}
A. Function Height
description&solution
输出\(\lceil\frac nk\rceil\) 。
code
#include<cstdio>
#include<algorithm>
using namespace std;
int main(){
long long n,k;
scanf("%I64d%I64d",&n,&k);
printf("%I64d\n",(k+n-1)/n);
return 0;
}