Codeforces Round #818 (Div. 2) A-F
Codeforces Round #818 (Div. 2) A-F
题目
A
问有多少对\(1\leq a,b \leq n\),满足\(\frac{lcm(a,b)}{gcd(a,b)}\leq3\)
已知\(lcm(a,b)=a*b/gcd(a,b)\),原式可化为\(\frac{a*b}{gcd(a,b)^2} \leq3\)
令\(d=gcd(a,b),a=A*d,b=B*d,A,B互质\),原式可化为\(A*B<=3\),则\(A,B\)只可分别为\((1,1)(1,2)(2,1)(1,3)(3,1)\),\((1,1)\)共有n种组合,\((1,2),(2,1)\)各有\(n/2\)的组合,\((1,3)(3,1)\)各有\(n/3\)个组合,因此最终答案
$ n + (n/2+n/3)*2$
void Solve(){
ll n;cin>>n;
cout<<(n/2+n/3)*2+n<<endl;
}
B
问对于一个边长为n的正方形点阵,给定一个位置\((r,c)\)为\(X\),求放置最少的\(X\)的个数使得对于任意长为k,宽为1的矩形里至少有一个\(X\), 输出放置后的图形。
分析样例可以看出斜着放最优,因此每次连续斜着放,然后对于已知点横向每k个放一个\(X\),保证最小放置。
int n,k,r,c;
char g[maxn][maxn];
int vis[maxn][maxn];
void dfs(int x,int y){
if(vis[x][y]) return;
vis[x][y]=1;g[x][y]='X';
dfs((x+1)%n,(y+1)%n);
}
void solve(){
cin>>n>>k>>r>>c;
r--;c--;
rep(i,0,n-1){
rep(j,0,n-1){
vis[i][j]=0;
g[i][j]='.';
}
}
while(1){
if(vis[r][c]) break;
dfs(r,c);
r=(r+k)%n;
}
rep(i,0,n-1){
rep(j,0,n-1) putchar(g[i][j]);
pts;
}
}
C
给定长度为n的数组a,b;对于每一个\(a_i\),如果\(a_i\leq a_{(i+1)\%n}\)那么\(a_i\)可以变为\(a_i + 1\),询问是否可以将数组a变为数组b
首先a种元素只能变大,因此如果\(a_i>b_i\)则非法。
其次因为\(a_i\)大小由\(a_{i+1}\)决定,且最终\(a_i\)最大为\(a_{i+1}+1\),因此当\(a_i<b_i \& b_i>b_{i+1}+1\)时,非法。
其余情况均合法。
int Solve(){
int n;cin>>n;
vector<int>a(n+1);
vector<int>b(n+2);
rep(i,1,n) cin>>a[i];
rep(i,1,n) cin>>b[i];
rep(i,1,n) {
if(a[i]>b[i]) return 0;
}
b[n+1]=b[1];
rep(i,1,n){
if(b[i]>b[i+1]+1 && a[i]<b[i]) return 0;
}
return 1;
}
D
有 \(2^n\)个人进行二进一淘汰赛,每一场淘汰赛分为左边和右边,你可以决定每一场淘汰赛的双方及左边赢还是右边赢,你的目标是让最后胜出的人编号最小。
而主办方有k次修改比赛的机会,修改为将比赛双方位置互换,并且主办方希望最后胜出的选手编号尽可能地大。
对于每一个n,你需要给出在你精心设计比赛后,主办方修改k次比赛后能得到的最大的选手编号。
题意可化为一颗二叉树,当k=0时,最小编号为1,当k=1时,那么对于参赛者1走n层,每一层的比赛均可以修改来源,那么在这n层种可以有\(C(n,1)\)种选择改变胜者,那么最大的标号就是\(1+C(n,1)\),以此类推,若k=2,那么相当于在n层中间选两层进行方向转变,那么有\(C(n,2)\)种选法,此时需要将这些选法一一赋值,故此时最大标号为\(1+C(n,1)+C(n,2)\)
综上,答案即为\(\sum_{i=0}^{min(n,k)} C(n,i)\)
void solve(){
com.build();
int n,k;
cin>>n>>k;
ll ans=1;
rep(i,1,min(n,k)){
ans+=com.C(n,i);
ans%=mod;
}
cout<<ans;
}
E
给定n,求对于所有\(a+b+c=n\)的三元组\((a,b,c)\)求\(\sum lcm(c,gcd(a,b))\)
对于\(lcm(c,gcd(a,b))\),我们令
\(d=gcd(a,b),a=x*d,b=y*d,gcd(x,y)=1\)
已知一个数的因子是根号级的,那么我们可以枚举\(c\),然后再遍历\(n-c\)的因子作为d,可以得到对应的\(lcm\),则\(x+y=(n-c)/d\),那么剩下即求如何将\((n-c)/d\)分为两个互质的数的和。
因为\(x,y\)互质,则\(gcd(x,y)=gcd(x+y,y)=gcd((n-c)/d,y)=1\),那么将一个数\(z\)分成两个互质的数的和的方法数为\(\phi(z)\).
综上所述,枚举\(c\)再枚举\(n-c\)的因数\(d\),然后累计答案即可
int p[maxm],cnt=0;
int vis[maxm],phi[maxm];
void pre(){
rep(i,2,N){
if(!vis[i]) p[++cnt]=i,phi[i]=i-1;
rep(j,1,cnt){
if(1LL*i*p[j]>N) break;
vis[i*p[j]]=1;
if(i%p[j]==0) {phi[i*p[j]]=phi[i]*p[j];break;}
else phi[i*p[j]]=phi[i]*phi[p[j]];
}
}
}
vector<int>G[maxn];
int gcd(int a,int b){return b? gcd(b,a%b):a;}
ll lcm(int a,int b){
return 1LL*a*b/gcd(a,b);
}
void Solve(){
pre();
int n;cin>>n;
rep(i,1,n){
for(int j=i;j<=n;j+=i){
G[j].pb(i);
}
}
ll ans=0;
rep(i,1,n) {
for(auto x:G[n-i]){
ans=(ans + 1LL*lcm(i,x) * phi[(n-i)/x])%mod;
}
}
cout<<ans<<endl;
}
F
给你一个长为n初始全为0的b数组,和m条边\((u,v)\),每次可将\(b[u],b[v]\)其中一个\(+1\),一个\(-1\)。
再给你两个长为n的s,a数组,问你是否存在,全部有的边操作后,对于所有的\(s_i=1\),都有\(a_i=b_i\),并输出所选择的边,每条边只能用一次。
首先对于一个\(+1\)一个\(-1\)操作,我们将操作从b数组对应到a数组,相当于对\(a[u],a[v]\)都\(-1\),再选择一个\(+2\),看最后a数组中是否所有数都能变成0,因此当变化后有\(a_i\)为奇数时,不存在解.
此时变化为匹配问题,每条边对应两个点,但每次匹配一个点,对于\(s_i=1\)的点,每个点应该被匹配\(a_i/2\)次。
可建模为网络流问题:
左边为m条边对应的点的标号,源点到这m条边容量为1,代表每条边只能用一次;
右边为数组a的n个点,每条边向其连接的两个点连一条容量为1的边,通过流时表示选择该点进行一次\(+2\);
对于\(s_i=1\)的点我们将其向汇点连接一条容量为\(a_i/2\)的边,表示该点需要经过\(a_i/2\)次,最后当这些边满流时,情况可解;
同时因为所有的边都要用上,那么对于\(s_i\)为0的点我们设立一个中继点,所有\(s_i=0\)的点连向该中继点,流量为inf,表示该点因为不受约束可无限选,然后将该中继点连向汇点,容量为\(m-\sum a_i/2[s_i=1]\),表示剩下的不受约束的点最多可以贡献这么多的流量。
最后当最大流为m时,表示所有边均被选中且\(a_i\)满足条件,然后在网络流图中找对应的边输出方案即可。
此时所有\(s_i=1\)的点的\(\sum a_i/2\leq m\),否则选了全部的边也不能满足情况,无解。
struct FLOW{
struct Edge{
int u,v,nxt,w;
}G[maxm];
int last[maxn],cur[maxn],cnt;
int ST,ED,n;
queue<int>q;
int dep[maxn];
void pre(int num){
cnt=1;
n=num;
rep(i,1,n) last[i]=cur[i]=0;
}
void add(int u,int v,int w){
G[++cnt]={u,v,last[u],w};
last[u]=cnt;swap(u,v);w=0;
G[++cnt]={u,v,last[u],w};
last[u]=cnt;
}
int dfs(int now,int lim){
if(now==ED) return lim;
int Sum=0;
for(int i=cur[now];i;i=G[i].nxt){
int v=G[i].v,w=G[i].w;
if(dep[v]==dep[now]+1 && w>0){
int tmp=dfs(v,min(lim,w));
Sum+=tmp;lim-=tmp;
G[i].w-=tmp;G[i^1].w+=tmp;
}
cur[now]=i;
if(!lim) break;
}
if(!Sum) dep[now]=-1;
return Sum;
}
int bfs(){
while(!q.empty()) q.pop();
rep(i,1,n) dep[i]=-1,cur[i]=last[i];
dep[ST]=1;q.push(ST);
while(!q.empty()){
int now=q.front();q.pop();
for(int i=last[now];i;i=G[i].nxt){
int v=G[i].v,w=G[i].w;
if(dep[v]==-1&&w>0) {
dep[v]=dep[now]+1;q.push(v);
}
}
}
return dep[ED]!=-1;
}
int Dinic(){
int Sum=0;
while(bfs())
Sum+=dfs(ST,inf);
return Sum;
}
void Out(vector<P> p,int m,vector<int> s){
rep(i,1+m,n+m){
int u=i-m;
for(int j=last[i];j;j=G[j].nxt){
int v=G[j].v,w=G[j].w;
if(v<=m && w){
if(u!=p[v].first) swap(p[v].first,p[v].second);
printf("%d %d\n",p[v].first,p[v].second);
}
}
}
}
}max_flow;
void solve(){
int n,m;cin>>n>>m;
vector<int>a(n+1),s(n+1);
vector<P>Path(m+1);
rep(i,1,n) cin>>s[i];
rep(i,1,n) cin>>a[i];
rep(i,1,m){
int u,v;cin>>u>>v;
Path[i]={u,v};
a[u]--,a[v]--;
}
rep(i,1,n){
if((a[i]&1) && (s[i])) {ptn;return;}
}
int st,ed,tmp,tep=0;
max_flow.pre(m+n+3);
st=n+m+1,ed=st+1,tmp=st+2;
rep(i,1,m){
max_flow.add(st,i,1);
max_flow.add(i,m+Path[i].first,1);
max_flow.add(i,m+Path[i].second,1);
}
rep(i,1,n){
if(s[i]) max_flow.add(m+i,ed,-a[i]/2),tep+=-a[i]/2;
else max_flow.add(m+i,tmp,inf);
}
max_flow.add(tmp,ed,m-tep);
max_flow.ST=st;max_flow.ED=ed;
tmp=max_flow.Dinic();
if(tmp!=m || tep>m) ptn;
else{
pty;
max_flow.Out(Path,m,s);
}
}