Codeforces Round #841 (Div. 2) and Divide by Zero 2022

Preface

这场Div2怎么感觉难度和Div3一样,ABCD都是SB题一眼秒,可惜D下标\(n,m\)弄混挂了一发

E本来也是秒出的正解,但是实现的时候一个细节挂了(自作聪明,后来结束前5min改出来了,结果2000pts的题喜提880pts)

终于结束了艰难的7连掉,可喜可贺、可喜可贺

补题的时候一看F也很simple,不过我肯定是观察不出可以插值的性质的说


A. Joey Takes Money

刚开始一眼看成最小的还愣了下

后来发现是求最大,那么直接把数都分到一个上面,其他都是\(1\)即可

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

B. Kill Demodogs

首先很容易发现只要一直向右向下交替着走就可以得到最大值

写出计算答案的式子,\(\sum_{i=1}^{n-1} [i^2+i(i+1)]+n^2=\sum_{i=1}^{n-1} [2i^2+i]+n^2=\frac{(n-1)n(2n-1)}{3}+\frac{n(n-1)}{2}+n^2\)

刚开始还被Note, you firstly multiply by \(2022\) and only after that take the remainder.骗了,以为不能直接乘逆元

后来发现显然是没关系的,当时还写的繁琐了点

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int mod=1e9+7;
int t,n;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; scanf("%d",&n); int coef[3]={n-1,n,2*n-1};
		for (i=0;i<3;++i) if (coef[i]%3==0) { coef[i]/=3; break; }
		int ans=1LL*n*(n-1)/2LL%mod; (ans+=1LL*n*n%mod)%=mod;
		int mul=1; for (RI i=0;i<3;++i) mul=1LL*mul*coef[i]%mod;
		(ans+=mul)%=mod; printf("%d\n",2022LL*ans%mod);
	}
	return 0;
}

C. Even Subarrays

不难发现约束个数是奇数的数就是所有的完全平方数

由于值域是\([1,n]\)的数的异或和是\([0,2n]\)的,因此可以直接根号复杂度枚举完全平方数\(t\),用总区间个数减去所有不合法的区间数

考虑枚举右端点\(i\)\(pfx_i\)表示\([1,i]\)的前缀异或和,那么我们只要每次找到所有出现在\(i\)之前的、值为\(pfx_i\oplus t\)\(pfx_j\)的数量即可

直接开个桶统计下即可,复杂度\(O(n\sqrt n)\)

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=400005;
int t,n,a[N],pfx[N],bkt[N]; long long ans;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; for (scanf("%d",&n),i=1;i<=n;++i)
		scanf("%d",&a[i]),pfx[i]=pfx[i-1]^a[i];
		int lim=1; while (lim<=n) lim<<=1;
		for (ans=1LL*n*(n+1)/2LL,j=0;1LL*j*j<lim;++j)
		{
			int tar=j*j; for (i=0;i<lim;++i) bkt[i]=0;
			for (bkt[0]=i=1;i<=n;++i) ans-=bkt[pfx[i]^tar],++bkt[pfx[i]];
		}
		printf("%lld\n",ans);
	}
	return 0;
}

D. Valiant's New Map

答案一眼具有二分性,考虑怎样验证\(x\)是否合法

一个显然的套路,我们把所有\(a_{i,j}\ge x\)的位置\((i,j)\)的值设为\(1\),否则设为\(0\)

判断一个\(x\times x\)子矩阵是否合法就只要看它里面所有数的和是否为\(x^2\)即可

利用二位前缀和可以轻松做到\(O(nm)\)的检验

#include<cstdio>
#include<vector>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=1e6+5;
int t,n,m,ans; vector <int> a[N],sum[N];
inline bool check(CI x)
{
	RI i,j; for (i=1;i<=n;++i) for (j=1;j<=m;++j) sum[i][j]=a[i][j]>=x;
	for (i=1;i<=n;++i) for (j=1;j<=m;++j) sum[i][j]+=sum[i][j-1];
	for (j=1;j<=m;++j) for (i=1;i<=n;++i) sum[i][j]+=sum[i-1][j];
	for (i=x;i<=n;++i) for (j=x;j<=m;++j)
	if (sum[i][j]-sum[i-x][j]-sum[i][j-x]+sum[i-x][j-x]==x*x) return 1;
	return 0;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=n;++i)
		for (a[i].resize(m+1),j=1;j<=m;++j) scanf("%d",&a[i][j]);
		for (i=0;i<=n;++i) sum[i].resize(m+1);
		int l=1,r=min(n,m),mid; while (l<=r)
		if (check(mid=l+r>>1)) ans=mid,l=mid+1; else r=mid-1;
		printf("%d\n",ans);
	}
	return 0;
}

E. Graph Cost

很simple的题,刚开始写完贪心然后一直挂以为要写DP

结果后面想DP的时候反而验证了贪心是对的,然后一顿排查三行的代码发现求边数的地方写挂了,浪费1h

一个naive的想法就是我们从大到小考虑所有\(k\),求出它对应的边数,然后能加多少就加多少

考虑证明这个贪心的正确性,我们可以把一种选择看作容量为\(k\),代价为\(k+1\)的物品,最后要求的就是填满容量为\(m\)的背包的最小代价

若这个贪心是错的,假设可以把一对选择容量\(x,y(x<y)\)的方案修改为选\(p,q(p<q)\),此时满足\(x<p<q<y\)

由于方案的替换不能改变容量,因此\(x+y=p+q\),而其实比较两种方案的代价,\(x+1+y+1\)\(p+1+q+1\)显然是相等的

因此我们证明了不存在更优的替换,因而证明了贪心的正确性

那么接下来的问题就是求\([1,n]\)\(\gcd=k\)的数对数目了,刚开始脑抽了写了个容斥的做法挂了(其实应该是可以做的,但是可能我写的不好)

后来发现我们设\(t=\lfloor\frac{n}{k}\rfloor\),即我们找出所有含有因数\(k\)\(t\)个数,把它们全部除以\(k\)后,只要统计互质的数对数量即可

不难发现这就是\(\sum_{i=2}^t \phi(i)\),因此预处理出欧拉函数的前缀和即可

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

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=1e6+5;
int prime[N],phi[N],cnt,t,n; long long m,ans,sum[N]; bool vis[N];
inline void init(CI n)
{
    RI i,j; for (i=2;i<=n;++i)
    {
        if (!vis[i]) prime[++cnt]=i,phi[i]=i-1;
        for (j=1;j<=cnt&&i*prime[j]<=n;++j)
        {
            vis[i*prime[j]]=1; if (i%prime[j]) phi[i*prime[j]]=phi[i]*(prime[j]-1);
            else { phi[i*prime[j]]=phi[i]*prime[j]; break; }
        }
        sum[i]=sum[i-1]+phi[i];
    }
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (init(1000000),scanf("%d",&t);t;--t)
	{
		RI i,j; for (scanf("%d%lld",&n,&m),ans=0,i=n;i>1;--i)
		{
			long long t=min(sum[n/i],m)/(i-1); ans+=t*i; m-=t*(i-1);
		}
		printf("%lld\n",m?-1:ans);
	}
	return 0;
}

F. Function Sum

首先我们不考虑数据范围,怎样暴力地算出答案

一个显然的做法就是对于每个位置的每种取值分别考虑贡献

枚举一个位置\(p\),满足\(a_p=v\)的条件下的贡献,枚举一下前面后面符合题目要求的位置的个数,很容易列出式子:

\[ans=\sum_{v=1}^k v\times[\sum_{p=1}^n \sum_{i=0}^{p-1}\sum_{j=i+1}^{n-p} C_{p-1}^i\times C_{n-p}^j\times (v-1)^i\times (k-v+1)^{p-i-1}\times (k-v)^j\times v^{n-p-j}] \]

这样的复杂度是\(O(n^3k)\)的,显然不可接受

但是我们细细观察,发现最内层的式子里所有幂指数的和是\(n-1\),而组合数和\(v\)的取值又是无关的

那么我们就知道,最内层的式子关于\(v\)就是个\(n-1\)次多项式

然后再经过三次求和,得到的其实就是个\(n+2\)次多项式,则说明最后的答案其实也就是个\(n+2\)次多项式

那么做法显而易见了,我们暴力算出\(0\sim n+2\)的答案,然后拉格朗日插值求出\(k\)的时候的答案即可

总复杂度\(O(n^5)\)

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=55,mod=998244353;
int n,k,C[N][N],x[N],y[N];
inline int sum(CI x,CI y)
{
	return x+y>=mod?x+y-mod:x+y;
}
inline int sub(CI x,CI y)
{
	return x-y<0?x-y+mod:x-y;
}
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;
}
inline void init(CI n)
{
	for (RI i=0,j;i<=n;++i) for (C[i][0]=j=1;j<=i;++j)
	C[i][j]=sum(C[i-1][j],C[i-1][j-1]);
}
inline int solve(CI n,CI k,int ret=0)
{
	for (RI v=1,p,i,j;v<=k;++v)
	{
		int coef=0; for (p=1;p<=n;++p) for (i=0;i<p;++i) for (j=i+1;j<=n-p;++j)
		coef=sum(coef,1LL*C[p-1][i]*C[n-p][j]%mod*quick_pow(v-1,i)%mod*quick_pow(k-v+1,p-i-1)%mod*quick_pow(k-v,j)%mod*quick_pow(v,n-p-j)%mod);
		ret=sum(ret,1LL*v*coef%mod);
	}
	return ret;
}
inline int Lagrange(CI n,int *x,int *y,CI k)
{
	int ret=0; for (RI i=1;i<=n;++i)
	{
		int s1=1,s2=1; for (RI j=1;j<=n;++j) if (i!=j)
		s1=1LL*s1*sub(k,x[j])%mod,s2=1LL*s2*sub(x[i],x[j])%mod;
		(ret+=1LL*y[i]*s1%mod*quick_pow(s2)%mod)%=mod;
	}
	return ret;
}
int main()
{
	RI i; for (scanf("%d%d",&n,&k),init(n),i=0;i<=n+2;++i) x[i]=i,y[i]=solve(n,i);
	if (k<=n+2) printf("%d",y[k]); else printf("%d",Lagrange(n+2,x,y,k));
	return 0;
}

Postscript

终于摆脱了掉分魔咒,又可以尝试冲击紫名了

posted @ 2022-12-28 21:31  空気力学の詩  阅读(41)  评论(0编辑  收藏  举报