Educational Codeforces Round 50
G. Sources and Sinks
description
有一张(DAG),定义source为入度为零的点,sink为出度为零的点。保证source和sink的个数相等且不超过(20) 。定义匹配一个source和一个sink为从sink向source连一条有向边。问是否任意一个source与sink之间的完美匹配都可以使原图变成强连通。
(nle10^6)
solution
设source与sink各有(C)个,那么只需要保证这(2C)个点组成的图强连通就行了。
有一个结论是:对于任意一个source的集合(S) ,(|S| eq0,|S| eq 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的数的个数。
(Tle10^5,nle10^{18})
solution
首先莫比乌斯随便演一下答案就是(sum_{i=1}^{infty}mu(i)(sqrt[i]n-1)) 。
直接算的话复杂度(O(Tlog^2n))(每个质因子二分根号的那个值)可能过不了。
把询问离线,从大到小做,可以省去一个二分。
复杂度(O(Tlog T+Tlog 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
",ans[i]);
return 0;
}
E. Covered Points
description
给你(n)条线段,问你有多少个点被这些线段覆盖了。
(nle1000,)坐标范围(le10^6)
solution
先不考虑算重,那么一条直线的贡献是(gcd(|x_1-x_2|,|y_1-y_2|)+1) 。
然后因为两条不重合直线至多一个交点,所以可以直接解出来交点,然后判重即可。
算交点直接联立两个一般式解方程,一般式用两点式求。
复杂度(O(n^2log 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
",ans);return 0;
}
D. Vasya and Arrays
description
给你两个数组(A,B) ,你的每次操作是可以把连续一段数合在一起,值为这些数的和。
求把两个数组变成相同后数组的最长长度。
(nle3 imes10^5,1le a_i,b_ile10^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
",ans);
else puts("-1");
return 0;
}
C. Classy Numbers
description
定义一个数是classy的当且仅当其在十进制表示下至多有(3)个非零位。
(T)组询问,每次问([L_i,R_i])之间有多少个classy的数。
(Tle10^4,L_i,R_ile10^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
",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)步,求最多可以斜着走多少步。
(qle10^4,n,m,kle10^{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
",k<x?-1:k);
}
return 0;
}
A. Function Height
description&solution
输出(lceilfrac nk ceil) 。
code
#include<cstdio>
#include<algorithm>
using namespace std;
int main(){
long long n,k;
scanf("%I64d%I64d",&n,&k);
printf("%I64d
",(k+n-1)/n);
return 0;
}