Codeforces Round 942 (Div. 1)

Preface

你们有没有这样的懒狗啊,4/30打的比赛拖到5/8才来写博客,再拖下去感觉题意都要忘个精光了

这场刚好因为去了武汉,因此全队订了一个三人间,外接大脑直接Div1出四题,狠狠地之前犯病调回紫的号打了回去

后面有空一直在琢磨D的做法,最后也是想到了通过断掉环上的一条边来得到一个很简单的讨论做法,不过比赛的时候肯定没时间写的说


A. Permutation Counting

不难发现最优的序列一定先是某个排列,然后将这个排列一直循环重复放

最后一定是某些数出现了\(k\)次,剩下的数出现了\(k+1\)次,直接二分找到最大的\(k\)即可

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define int long long
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=500005;
int t,n,k,a[N];
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i; for (scanf("%lld%lld",&n,&k),i=1;i<=n;++i) scanf("%lld",&a[i]);
		auto check=[&](CI lim)
		{
			int cur=0; for (RI i=1;i<=n;++i) cur+=max(0LL,lim-a[i]); return cur<=k;
		};
		int l=1,r=2e12,ret=0,mid; while (l<=r)
		if (check(mid=l+r>>1)) ret=mid,l=mid+1; else r=mid-1;
		if (ret==0) { puts("0"); continue; }
		for (i=1;i<=n;++i) if (a[i]<ret) k-=(ret-a[i]),a[i]=ret;
		sort(a+1,a+n+1,greater <int>());
		for (i=1;i<=n;++i) if (a[i]==a[n]&&k>0) --k,++a[i];
		int ans=n*a[n]-(n-1);
		for (i=1;i<=n;++i) if (a[i]!=a[n]) ++ans;
		printf("%lld\n",ans);
	}
	return 0;
}

B1. Reverse Card (Easy Version)

手玩一下式子会发现\(a\)必须是\(b\)的倍数,不妨设\(a=kb\),此时\(\gcd(a,b)=b\)

化一下式子会发现此时的限制就是\(b\mid(k+1)\),直接枚举\(b\)的值计算即可

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,n,m;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		LL ans=0; scanf("%d%d",&n,&m);
		for (RI b=1;b<=m&&1LL*b*(b-1)<=n;++b)
		if (ans+=1LL*(n+b)/(1LL*b*b),b==1) --ans;
		printf("%lld\n",ans);
	}
	return 0;
}

B2. Reverse Card (Hard Version)

看到\(\gcd\),经典的套路就是直接枚举它的值,不妨设\(g=\gcd(a,b)\),则\(a=p\times g,b=q\times g\),且\(p,q\)互质

那么此时的限制变为了\((p+q)\times g\mid q\times g^2\),即\((p+q)\mid q\times g\)

\(p,q\)互质可以推出\((p+q),q\)一定也互质,那么上面的式子成立当且仅当\((p+q)\mid g\)

不妨设\(g=k\times (p+q)\),则\(a=k\times (p^2+pq),b=k\times (pq+q^2)\),显然\(p\le \sqrt n,q\le \sqrt m\),因此可以直接枚举

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=2e6+5;
int t,n,m;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%d",&n,&m); LL ans=min(n,m)/2LL;
		for (RI p=1;p*p<=n;++p) for (RI q=1;q*q<=m;++q)
		{
			if (p==q||__gcd(p,q)!=1) continue;
			int x=p*(p+q),y=q*(p+q);
			ans+=min(n/x,m/y);
		}
		printf("%lld\n",ans);
		//for (RI a=1;a<=n;++a) for (RI b=1;b<=m;++b)
		//if ((b*gcd(a,b))%(a+b)==0&&a%b!=0&&b%a!=0) printf("a = %lld; b = %lld\n",a,b);
	}
	return 0;
}

C. Fenwick Tree

首先要观察到位置\(i\)上的数会产生贡献的永远是那\(\log\)个位置

而如果我们单独把这些位置拿出来看,会发现\(a_i\)对它们的贡献形如这样

第一轮是\(1,1,1,1,1,\cdots\);第二轮是\(1,2,3,4,5,\cdots\);第三轮是\(1,3,6,10,15,\cdots\);第四轮是\(1,4,10,20,35\)……

不难看出这玩意就是个高阶前缀和,本来以为只能用矩乘爆艹的,但后面徐神一指点发现就是个傻逼组合数

虽然组合数的下标\(k\)\(10^9\)的,但由于上标的范围只有\(20\),因此还是可以暴力计算

最后实现的时候注意预处理一下小范围每个数的逆元,不然复杂度会变成三个\(\log\)原地升天

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
typedef vector <vector <int>> Matrix;
const int N=200005,mod=998244353;
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;
}
int t,n,k,inv[35],a[N],b[N];
inline void init(CI n)
{
	for (RI i=1;i<=n;++i) inv[i]=quick_pow(i); inv[0]=1;
}
inline int C(int n,int m,int ret=1)
{
	for (RI i=1;i<=m;++i) ret=1LL*ret*(n-i+1)%mod*inv[i]%mod; return ret;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t),init(30);t;--t)
	{
		RI i,j,s; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",&b[i]);
		auto lowbit=[&](CI x)
		{
			return x&(-x);
		};
		for (i=1;i<=n;++i)
		{
			a[i]=b[i];
			for (j=i,s=0;j<=n;j+=lowbit(j),++s)
			b[j]=sub(b[j],1LL*a[i]*C(k-1+s,s)%mod);
		}
		for (i=1;i<=n;++i) printf("%d%c",a[i]," \n"[i==n]);
	}
	return 0;
}

D. Long Way to be Non-decreasing

很好的一个题,本来以为是个大力数据结构,但后面发现其实动点脑子后是个很自然很好写的题

首先由于这个操作很神秘,每次选择一个子集感觉根本难以维护,用DP之类的方法也完全不知道怎么设计状态

那么不妨考虑对于这类单调不降问题的通法:按序贪心

考虑从前往后依次确定每个数的值,在大于前一个数的基础上让该数尽量小

但是现在又有问题就是我们很难求出从某个数变到另一个数的最小步数,使得上述贪心难以实现

不过越是看起来无解的问题实际上越简单,事实上我们只需要在外层加一个二分答案,把最优化问题转化为判定性问题即可

因此现在的难点在于,给定x y k,快速回答数\(x\)能否在\(k\)次变换之内变成\(y\)

注意给出的图是一个基环内向森林,这种图不太好直接转化为有根树处理,因此我们不妨把它变成外向森林后把询问的两个点调换一下即可

因此对于每个连通块,其实无非两种情况,要么是个有根树,要么是个基环外向树

前者的情况很好判断,一个点能否在\(k\)步内到达另一个点只要判断下是否是祖先关系,然后深度差是否\(\le k\)即可

后者本来以为是要分每个点在/不在环上讨论的,但这样实在过于繁琐

经典的处理方法就是断掉基环树上的一条边\(u\to v\),分\(x\to y\)经过/不经过这条边来讨论

不经过的情况和树同理,而经过的情况画个图手玩一下也很容易得到贡献的计算方式(因为\(v\)一定是树的根节点,因此可以少判很多情况)

总复杂度\(O((n+m)\log m)\)

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=1e6+5,INF=1e9;
int t,n,m,a[N],b[N],fa[N],dep[N],L[N],R[N],U[N],V[N],idx; vector <int> v[N];
class FileInputOutput
{
    private:
        static const int S=1<<21;
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
        char Fin[S],*A,*B;
    public:
        Tp inline void read(T& x)
        {
            x=0; char ch; while (!isdigit(ch=tc()));
            while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
        }
        #undef tc
}F;
inline int getfa(CI x)
{
	return fa[x]!=x?fa[x]=getfa(fa[x]):x;
}
inline void DFS(CI now,CI fa=0)
{
	L[now]=++idx; for (auto to:v[now])
	if (to!=fa) dep[to]=dep[now]+1,DFS(to,now); R[now]=idx;
}
inline int ask(CI y,CI x)
{
	if (getfa(x)!=getfa(y)) return INF; int id=getfa(x),res=INF;
	if (L[x]<=L[y]&&L[y]<=R[x]) res=min(res,dep[y]-dep[x]);
	if (L[x]<=L[U[id]]&&L[U[id]]<=R[x]) res=min(res,dep[U[id]]-dep[x]+1+dep[y]);
	return res;
}
inline bool check(CI k)
{
	int p=1; for (RI i=1;i<=n;++i)
	{
		while (p<=m&&ask(a[i],p)>k) ++p;
		if (p>m) return 0;
	}
	return 1;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (F.read(t);t;--t)
	{
		RI i; for (F.read(n),F.read(m),i=1;i<=n;++i) F.read(a[i]);
		for (i=1;i<=m;++i) F.read(b[i]),fa[i]=i,v[i].clear();
		for (i=1;i<=m;++i)
		{
			if (getfa(i)==getfa(b[i])) U[getfa(i)]=b[i],V[getfa(i)]=i;
			else fa[getfa(i)]=getfa(b[i]),v[b[i]].push_back(i);
		}
		for (i=1;i<=m;++i) if (i==V[getfa(i)]) idx=dep[i]=0,DFS(i);
		int l=0,r=m,mid,ret=-1; while (l<=r)
		if (check(mid=l+r>>1)) ret=mid,r=mid-1; else l=mid+1;
		printf("%d\n",ret);
	}
	return 0;
}

Postscript

被徐神上强度了,以后CF不能摆烂不打了的说,不然下次比赛又要开演了

posted @ 2024-05-08 19:30  空気力学の詩  阅读(98)  评论(0编辑  收藏  举报