Codeforces Round 955 (Div. 2, with prizes from NEAR!)

Preface

由于考试是真彻底结束了,但队友都还没考完,这两天没事只能VP下之前没打的比赛

这场总体感觉题好水,如果不是E想复杂了导致写了1h然后卡常卡了挺久,F看一眼就会了15min就能写完,感觉有机会2h内AK的


A. Soccer

签到,贪心让原来分数高的队先得分是最优的

#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,a,b,c,d;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%d%d%d",&a,&b,&c,&d);
		if (a>b) swap(a,b),swap(c,d);
		if (c>d) puts("NO"); else puts("YES");
	}
	return 0;
}

B. Collatz Conjecture

我们可以先重复进行这样一个暴力过程,每次找到 \(x\) 下一个能被 \(y\) 整除的数,然后变换过去

不发现这个过程在重复 \(\sqrt x\) 级别后 \(x\) 就会变成 \(1\),剩下的部分是个 \(1\to k\to 1\) 的循环,可以直接求解

#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,x,y,k;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%d%d",&x,&y,&k);
		while (x!=1&&k)
		{
			int tar=(x/y+1)*y;
			if (k<tar-x) x+=k,k=0;
			else
			{
				k-=tar-x; x=tar;
				while (x%y==0) x/=y;
			}
		}
		if (!k) { printf("%d\n",x); continue; }
		printf("%d\n",1+k%(y-1));
	}
	return 0;
}

C. Boring Day

拿个队列维护下当前极长的且未被选择的段,考虑当选中的值之和 \(\ge l\) 时直接贪心地划分掉一定是最优的

同时当出现选中的值之和 \(>r\) 的情形时从队首弹出元素即可,总复杂度 \(O(n)\)

#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=200005;
int t,n,l,r,a[N],q[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d%d%d",&n,&l,&r),i=1;i<=n;++i) scanf("%d",&a[i]);
		RI H=1,T=0; int sum=0,ans=0;
		for (i=1;i<=n;++i)
		{
			q[++T]=a[i]; sum+=a[i];
			while (sum>r) sum-=q[H++];
			if (sum>=l) ++ans,sum=0,H=1,T=0;
		}
		printf("%d\n",ans);
	}
	return 0;
}

D. Beauty of the mountains

很显然的一个题,不妨令 \(diff\) 为初始时两种贡献的差值的绝对值

对于每个 \(k\times k\) 的子矩阵,可以用二维前缀和求出它内部两种颜色的差值 \(c_i\),不难发现每个子矩阵造成的贡献一定是 \(c_i\) 的倍数

由裴蜀定理,设 \(g=\gcd(c_i)\),则有解的充要条件为 \(g\mid diff\),模拟上述过程即可

#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=505;
int t,n,m,k,a[N][N],s[N][N]; char c[N][N];
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",&n,&m,&k); LL diff=0;
		for (i=1;i<=n;++i) for (j=1;j<=m;++j) scanf("%d",&a[i][j]);
		for (i=1;i<=n;++i) scanf("%s",c[i]+1);
		for (i=1;i<=n;++i) for (j=1;j<=m;++j)
		{
			s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(c[i][j]=='1'?1:-1);
			if (c[i][j]=='1') diff+=a[i][j]; else diff-=a[i][j];
		}
		if (diff==0) { puts("YES"); continue; }
		diff=abs(diff); int g=0;
		for (i=1;i+k-1<=n;++i) for (j=1;j+k-1<=m;++j)
		{
			int tmp=s[i+k-1][j+k-1]-s[i-1][j+k-1]-s[i+k-1][j-1]+s[i-1][j-1];
			tmp=abs(tmp); g=__gcd(g,tmp);
		}
		if (g==0) puts("NO"); else puts(diff%g?"NO":"YES");
	}
	return 0;
}

E. Number of k-good subarrays

感觉我又把简单题做复杂了,没去写数位DP去写了个很神秘的东西,最后常数还贼大卡了半天,只能说不愧是我

首先不难想到倍增,假设之前求出了 \([0,2^m-1]\) 中,贡献为 \(0,1,2,\dots,k\) 的答案

则后面一段 \([2^m,2^{m+1}-1]\) 内部的 贡献为 \(0,1,2,\dots,k\) 的答案后可以直接拿来用,剩下的就是考虑左端点在 \([0,2^m-1]\) 中,右端点在 \([2^m,2^{m+1}-1]\) 中的贡献了

不妨直接枚举计算贡献 \(\le i\) 的方案数,最后差分计算具体的方案数即可

这个问题等价于在某个区间 \([l,r]\) 中找到最小/最大的 \(x\) 满足 \(bit(x)>i\),利用一些二进制的性质我们可以 \(O(k)\) 直接求解

最小的就是把 \(l\) 的后缀依次改成全 \(1\);最大的就有些麻烦,需要找到 \(r\) 中所有 \(0\) 出现的位置然后把低位填成 \(1\);这部分的具体实现可以看代码

然后直接做复杂度是单组数据 \(O(k^3)\) 的,但我们可以把倍增过程预处理一下得到一个 \(O(k^3+t\times k^2)\) 的做法,卡卡常数可以通过

#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 mod=1e9+7;
int t,k; LL n; array <int,61> f[65]; map <LL,array <int,61>> rst;
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
inline void dec(int& x,CI y)
{
	if ((x-=y)<0) x+=mod;
}
inline int find_lst(const LL& l,const LL& r,CI k)
{
	LL x=r; int cnt=__builtin_popcountll(x);
	if (cnt>k) return 0;
	for (RI i=0;i<60;++i)
	if ((x>>i)&1)
	{
		--cnt; x^=(1LL<<i);
		if (cnt+i>k)
		{
			x|=((1LL<<i)-1);
			if (x<l) return (r-l+1)%mod;
			else return (r-x)%mod;
		}
	}
	return (r-l+1)%mod;
}
inline int find_fst(const LL& l,const LL& r,CI k)
{
	LL x=l; int cnt=__builtin_popcountll(x);
	if (cnt>k) return 0;
	for (RI i=0;i<60;++i)
	{
		if ((x>>i)&1) --cnt,x^=(1LL<<i);
		if (cnt+i+1>k)
		{
			x|=((1LL<<i+1)-1);
			if (x>r) return (r-l+1)%mod;
			else return (x-l)%mod;
		}
	}
	return (r-l+1)%mod;
}
inline void init(void)
{
	array <int,61> v={0}; v[0]=1; f[0]=v;
	LL x=0; for (RI i,j=1;j<61;x=x*2+1,++j)
	{
		array <int,61> u={0};
		for (u[0]=0,i=1;i<61;++i) u[i]=v[i-1];
		for (i=1;i<61;++i) inc(v[i],u[i]);
		for (i=1;i<61;++i) u[i]=1LL*find_lst(0,x,i)*find_fst(x+1,x*2+1,i)%mod;
		for (i=60;i>=1;--i) dec(u[i],u[i-1]);
		for (i=1;i<61;++i) inc(v[i],u[i]);
		f[j]=v;
	}
}
inline array <int,61> solve(const LL& n)
{
	if (rst.count(n)) return rst[n];
	int k=__lg(n+1); LL x=(1LL<<k)-1;
	if (n==x) return rst[n]=f[k]; else
	{
		RI i; array <int,61> v=f[k],tmp=solve(n-x-1),u={0};
		for (u[0]=0,i=1;i<61;++i) u[i]=tmp[i-1];
		for (i=1;i<61;++i) inc(v[i],u[i]);
		for (i=1;i<61;++i) u[i]=1LL*find_lst(0,x,i)%mod*find_fst(x+1,n,i)%mod%mod;
		for (i=60;i>=1;--i) dec(u[i],u[i-1]);
		for (i=1;i<61;++i) inc(v[i],u[i]);
		return rst[n]=v;
	}
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t),init();t;--t)
	{
		scanf("%lld%d",&n,&k);
		array <int,61> tmp=solve(n-1); int ans=0;
		for (RI i=0;i<=k;++i) inc(ans,tmp[i]);
		printf("%d\n",ans);
	}
	return 0;
}

F. Sorting Problem Again

很丁真的一个题,我们可以大力维护出数组中所有 \(a_i>a_{i+1}\) 的位置 \(i\) 的集合 \(S\),显然如果不存在这样的位置就说明数组有序

否则令 \(l=\min(S),r=\max(S)+1\),显然要操作的区间至少得包含 \([l,r]\),不妨令 \(mn=\min_\limits{l\le i\le r} a_i,mx=\max_\limits{l\le i\le r} a_i\)

由于分界点的性质,\([1,l-1],[r+1,n]\) 都是单调不降的连续段,因此最后要保留的前缀/后缀就很好计算了,直接在对应的部分里二分一下即可

单点修改和区间最值查询用线段树维护,总复杂度 \(O(n\log n)\)

#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=500005,INF=1e9;
int t,n,a[N],q,x,y; set <int> s;
class Segment_Tree
{
	private:
		int mn[N<<2],mx[N<<2];
	public:
		#define TN CI now=1,CI l=1,CI r=n
		#define LS now<<1,l,mid
		#define RS now<<1|1,mid+1,r
		inline void updata(CI pos,CI mv,TN)
		{
			if (l==r) return (void)(mn[now]=mx[now]=mv); int mid=l+r>>1;
			if (pos<=mid) updata(pos,mv,LS); else updata(pos,mv,RS);
			mn[now]=min(mn[now<<1],mn[now<<1|1]);
			mx[now]=max(mx[now<<1],mx[now<<1|1]);
		}
		inline int query_min(CI beg,CI end,TN)
		{
			if (beg<=l&&r<=end) return mn[now]; int mid=l+r>>1,ret=INF;
			if (beg<=mid) ret=min(ret,query_min(beg,end,LS));
			if (end>mid) ret=min(ret,query_min(beg,end,RS));
			return ret;
		}
		inline int query_max(CI beg,CI end,TN)
		{
			if (beg<=l&&r<=end) return mx[now]; int mid=l+r>>1,ret=-INF;
			if (beg<=mid) ret=max(ret,query_max(beg,end,LS));
			if (end>mid) ret=max(ret,query_max(beg,end,RS));
			return ret;
		}
		#undef TN
		#undef LS
		#undef RS
}SEG;
inline void solve(void)
{
	if (s.empty()) return (void)(puts("-1 -1"));
	int l=*s.begin(),r=*s.rbegin()+1;
	int mn=SEG.query_min(l,r),mx=SEG.query_max(l,r);
	if (mn<a[1]) l=1; else l=upper_bound(a+1,a+l,mn)-a;
	if (mx>a[n]) r=n; else r=lower_bound(a+r+1,a+n+1,mx)-a-1;
	printf("%d %d\n",l,r);
}
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]),SEG.updata(i,a[i]);
		for (s.clear(),i=1;i<n;++i) if (a[i]>a[i+1]) s.insert(i);
		for (solve(),scanf("%d",&q),i=1;i<=q;++i)
		{
			scanf("%d%d",&x,&y);
			if (x+1<=n&&a[x]>a[x+1]) s.erase(x);
			if (x-1>=1&&a[x-1]>a[x]) s.erase(x-1);
			a[x]=y; SEG.updata(x,a[x]);
			if (x+1<=n&&a[x]>a[x+1]) s.insert(x);
			if (x-1>=1&&a[x-1]>a[x]) s.insert(x-1);
			solve();
		}
	}
	return 0;
}

Postscript

继之前给祁神的几何专题胡了几个题面后,徐神也找我外包字符串的题面了,感觉能编题题面的Gal库存要不足了的说

posted @ 2024-07-02 19:40  空気力学の詩  阅读(121)  评论(0编辑  收藏  举报