Codeforces Round #818 (Div. 2)

Preface

今天是农历生日哒,然后明天就是阳历生日,二十岁了感觉还是很恍惚地搞不清未来的方向呢

今天云顶更新了,而且还回坑了炉石,但题目也不能忘记写啊


A. Madoka and Strange Thoughts

首先\(\frac{\operatorname{lcm}(a, b)}{\operatorname{gcd}(a, b)} \leq 3\Leftrightarrow \frac{a\times b}{\gcd(a,b)^2}\le 3\),设\(d=\gcd(a,b)\),则\(a=a'\times d,b=b'\times d\)

由于\(a',b'\)互质且\(a'\times b\le3\),不难发现取值只有\((1,1),(1,2),(2,1),(1,3),(3,1)\)这几种,即原来的\(a,b\)取值为\((x,x),(x,2x),(2x,x),(x,3x),(3x,x)\)这几种

答案就是\(n+2\times (\lfloor \frac{n}{2}\rfloor+\lfloor \frac{n}{3}\rfloor)\)

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
int t,n;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t) scanf("%d",&n),printf("%lld\n",1LL*n+2LL*(n/2)+2LL*(n/3));
	return 0;
}

B. Madoka and Underground Competitions

不难发现我们只需要填好一个\(k\times k\)的情况即可,剩下的直接复制到\(n\times n\)里即可

而此时我们要做到每行和每列均有且仅有一个X,假设上一个X填在\((x,y)\),那么下一个就填在\((x\operatorname{mod} k+1,y\operatorname{mod} k+1)\)即可保证要求

那么现在还需要\((r,c)\)一定有X,那我们只要把它在第一个矩阵中对应的格子\(((r-1)\operatorname{mod}k+1,(c-1)\operatorname{mod}+1)\)先填上X,再从它开始填后面的即可

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=505;
int t,n,k,r,c; char a[N][N],b[N][N];
inline void copy(CI x,CI y)
{
	for (RI i=1;i<=k;++i) for (RI j=1;j<=k;++j)
	b[x+i-1][y+j-1]=a[i][j];
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; scanf("%d%d%d%d",&n,&k,&r,&c); r=(r-1)%k+1; c=(c-1)%k+1;
		for (i=1;i<=k;++i) for (j=1;j<=k;++j) a[i][j]='.';
		for (a[r][c]='X',i=r==k?1:r+1,j=c==k?1:c+1;i!=r;i=i==k?1:i+1,j=j==k?1:j+1) a[i][j]='X';
		for (i=1;i<=n;i+=k) for (j=1;j<=n;j+=k) copy(i,j);
		for (i=1;i<=n;++i,putchar('\n')) for (j=1;j<=n;++j) putchar(b[i][j]);
	}
	return 0;
}

C. Madoka and Formal Statement

首先排除掉明显不合法的\(a_i>b_i\)的情形,我们发现对于任意一个需要增加的\(i\),若\(b_{i}<b_{i+1}+1\),则该位置必然不合法

否则必然存在一种构造方式,每次找到最小的一个位置进行操作即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,a[N],b[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
		bool flag=1; for (i=1;i<=n;++i) if (scanf("%d",&b[i]),b[i]<a[i]) flag=0;
		if (!flag) { puts("NO"); continue; }
		for (i=1;i<=n&&flag;++i) if (a[i]<b[i]&&b[i]>b[i==n?1:i+1]+1) flag=0;
		puts(flag?"YES":"NO");
	}
	return 0;
}

D. Madoka and The Corruption Scheme

由于初始顺序和比赛胜负关系两个都是随意控制的,我们不妨设每次比赛都是左边的获胜,只考虑安排初始顺序

借用题目中给出的那幅图,我们把胜利的树边称为红边,失败的树边称为黑边,不难发现如果主办方想让某个点获胜,那么从根节点到该点(叶节点)的路径上黑边的数量必须\(\le k\)

那么我们只需要找出有多少个叶节点到它们的黑边数量是\(\le k\)的,把小的数都填在这里就能得到答案了

考虑设\(f_{n,t}\)表示经过\(n\)条树边且黑色边的数目为\(t\)的方案数,考虑转移从某个点的两个子节点中转移来,其中这两条边一条为红一条为黑

则有转移方程:\(f_{n,t}=f_{n-1,t-1}+f_{n-1,t}\),这和杨辉三角的转移方程是相同的,即\(f_{n,t}=C_n^t\)

因此答案就是\(\sum_{i=0}^{\min(n,k)} C_n^i\)

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,mod=1e9+7;
int n,k,fac[N],ifac[N],ans;
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%d",&n,&k),fac[0]=i=1;i<=n;++i) fac[i]=1LL*fac[i-1]*i%mod;
	for (ifac[n]=quick_pow(fac[n]),i=n-1;~i;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
	for (i=0;i<=min(n,k);++i) (ans+=1LL*fac[n]*ifac[i]%mod*ifac[n-i]%mod)%=mod;
	return printf("%d",ans),0;
}

E. Madoka and The Best University

话说貌似和\(\gcd\)沾点边的数论题都是一个套路啊,提取后互质就完了……

考虑到\(c\)的特别性,先枚举\(c\),设\(d=\gcd(a,b)\),则\(a=d\times a',b=d\times b',\gcd(a',b')=1\)

由于\(d=\gcd(a,b)=\gcd(a+b,a)\),因此\(d|a+b\),即\(d|(n-c)\)

所以我们可以直接再枚举\(d\),答案就是\(\operatorname{lcm}(c,d)\times \phi(\frac{n-c}{d})\),因为\(a',b'\)互质等价于\(a'+b',b'\)互质,而\(a'+b'=\frac{n-c}{d}\)

复杂度\(O(n\sqrt n)\)

#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,mod=1e9+7;
int n,pri[N],phi[N],cnt,ans; bool vis[N]; vector <int> fac[N];
inline void init(CI n)
{
	for (RI i=2;i<=n;++i)
	{
		if (!vis[i]) pri[++cnt]=i,phi[i]=i-1;
		for (RI j=1;j<=cnt&&i*pri[j]<=n;++j)
		if (vis[i*pri[j]]=1,i%pri[j]) phi[i*pri[j]]=phi[i]*(pri[j]-1);
		else { phi[i*pri[j]]=phi[i]*pri[j]; break; }
	}
}
inline int gcd(CI n,CI m)
{
	return m?gcd(m,n%m):n;
}
inline int lcm(CI n,CI m)
{
	return 1LL*n*m/gcd(n,m)%mod;
}
int main()
{
	RI i,j; for (scanf("%d",&n),init(n),i=1;i<=n;++i)
	for (j=i;j<=n;j+=i) fac[j].push_back(i);
	for (i=1;i<=n-2;++i) for (int j:fac[n-i])
	(ans+=1LL*lcm(i,j)*phi[(n-i)/j]%mod)%=mod;
	return printf("%d",ans),0;
}

F. Madoka and The First Session

康复后没写过网络流相关的题目,因此完全没想到菜是原罪

首先考虑把原来的修改变为:将\(a_{u_i},a_{v_i}\)均减\(1\),然后选择其中的一个加\(2\),那么我们发现若此时\(a_i\)为奇数则无解

否则每个\(a_i\)应该被操作\(-\frac{a_i}{2}\)次,考虑用网络流建模来完成:

  • 左边为\(m\)条边对应的点,源点到它们连容量为\(1\)的边,代表每条边需要用一次
  • 右边为数组\(a\)的对应的\(n\)个点,每条边向其连接的两个点之间连容量为\(1\)的边,代表每条边的选择情况
  • 对于右边所有\(s_i=1\)的点向汇点连容量为\(-\frac{a_i}{2}\)的边,最后当这些边满流时则有解
  • 设立一个中继点,对于右边所有\(s_i=0\)的点向中继点连容量为\(\infty\)的边,因为这些点可以不受约束随便选
  • 最后将中继点向汇点连容量为\(m-\sum_\limits{s_i=1} -\frac{a_i}{2}\)的边,使得最后全局的满流为\(m\)

最后若最大流为\(m\)则有解,直接在图上输出方案即可,复杂度就是Dinic的那种你懂的快

#include<cstdio>
#include<iostream>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=10005,INF=1e9;
int n,m,s[N],a[N],u[N],v[N],sum,tmp;
namespace NF //Network Flow
{
	struct edge
	{
		int to,nxt,v;
	}e[N<<3]; int cnt=1,head[N<<1],cur[N<<1],q[N<<1],dep[N<<1],s,t;
	inline void addedge(CI x,CI y,CI z)
	{
		e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
		e[++cnt]=(edge){x,head[y],0}; head[y]=cnt;
	}
	#define to e[i].to
	inline bool BFS(void)
	{
		RI H=0,T=1; memset(dep,0,t+1<<2); q[dep[s]=1]=s;
		while (H<T)
		{
			int now=q[++H]; for (RI i=head[now];i;i=e[i].nxt)
			if (e[i].v&&!dep[to]) dep[to]=dep[now]+1,q[++T]=to;
		}
		return dep[t];
	}
	inline int DFS(CI now,CI tar,int dis)
	{
		if (now==tar) return dis; int ret=0;
		for (RI& i=cur[now];i&&dis;i=e[i].nxt)
		if (e[i].v&&dep[to]==dep[now]+1)
		{
			int dist=DFS(to,tar,min(dis,e[i].v));
			dis-=dist; ret+=dist; e[i].v-=dist; e[i^1].v+=dist;
		}
		if (!ret) dep[now]=0; return ret;
	}
	inline int Dinic(int ret=0)
	{
		while (BFS()) memcpy(cur,head,t+1<<2),ret+=DFS(s,t,INF); return ret;
	}
	inline void print(void)
	{
		RI i,j; for (j=m+1;j<=m+n;++j) for (i=head[j];i;i=e[i].nxt)
		if (to<=m&&e[i].v&&j-m!=u[to]) swap(u[to],v[to]);
		for (i=1;i<=m;++i) printf("%d %d\n",u[i],v[i]);
	}
	#undef to
};
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&s[i]);
	for (i=1;i<=n;++i) scanf("%d",&a[i]);
	for (i=1;i<=m;++i) scanf("%d%d",&u[i],&v[i]),--a[u[i]],--a[v[i]];
	for (i=1;i<=n;++i) if (s[i]&&(a[i]&1)) return puts("NO"),0;
	for (NF::s=n+m+1,tmp=n+m+2,NF::t=n+m+3,i=1;i<=m;++i)
	NF::addedge(NF::s,i,1),NF::addedge(i,m+u[i],1),NF::addedge(i,m+v[i],1);
	for (i=1;i<=n;++i) if (!s[i]) NF::addedge(m+i,tmp,INF);
	else NF::addedge(m+i,NF::t,-a[i]/2),sum+=-a[i]/2;
	if (sum>m) return puts("NO"),0;
	NF::addedge(tmp,NF::t,m-sum); int sum=NF::Dinic();
	if (sum!=m) return puts("NO"),0; 
	return puts("YES"),NF::print(),0;
}

Postscript

这两天没什么课就算了,明天开始甚至还要放中秋假,对于我们封校的地方来说形同虚设啊

实名羡慕那些可以到处面基社交的人awa……

posted @ 2022-09-09 16:08  空気力学の詩  阅读(51)  评论(0编辑  收藏  举报