AtCoder Regular Contest 172

Preface

开学了小溜一下之前没打的ARC,结果这场后面没有计数改成数论了又给我创飞了

这场的DE都太玄学了,属于是自己想半天一点屌思路没有然后看一眼题解就顿悟的类型

总结就是菜得发昏


A - Chocolate

挺有意思的签到题

考虑从大到小依次切,对于一个原来\(H'\times W'\)的块,为了尽量不浪费肯定是把它切下一个角来

不难发现剩余的部分可以分出三个更小的块,暴力模拟一下这个过程即可

至于为什么不是分两个块,因为我们是从大到小考虑的,如果分三块无解那么分两块肯定也无解

#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=1005;
int H,W,n,a[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i,j; for (scanf("%d%d%d",&H,&W,&n),i=1;i<=n;++i) scanf("%d",&a[i]);
	sort(a+1,a+n+1,greater <int>()); vector <pi> vec; vec.push_back(pi(H,W));
	for (i=1;i<=n;++i)
	{
		int len=1<<a[i]; bool flag=0;
		for (j=0;j<vec.size();++j)
		{
			auto [x,y]=vec[j];
			if (x>=len&&y>=len)
			{
				if (x-len>0) vec.push_back(pi(x-len,len));
				if (y-len>0) vec.push_back(pi(len,y-len));
				if (x-len>0&&y-len>0) vec.push_back(pi(x-len,y-len));
				vec.erase(vec.begin()+j); flag=1; break;	
			}
		}
		if (!flag) return puts("No"),0;
	}
	return puts("Yes"),0;
}

B - AtCoder Language

真·签到计数题,属于是连我看一眼都会做的程度了

不难发现一个合法的串需要满足,对于任意两个颜色相同的位置\(i,j(i<j)\),必须有\(n-(j-i+1)<k-1\)

因为若不满足上面的条件我们就可以在区间外部选出\(k-1\)个位置,然后最后\(1\)个位置就有至少两种选法了

不妨考虑从前往后确定,第一位的放法有\(L\)种,第二位的放法有\(L-1\)种,一直到第\(n-(k-1)\)位及之后的放法有\(L-[n-(k-1)]+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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int mod=998244353;
int n,k,l;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; scanf("%d%d%d",&n,&k,&l); int ans=1;
	for (i=0;i<n-(k-1);++i) ans=1LL*ans*(l-i)%mod;
	for (i=1;i<=k-1;++i) ans=1LL*ans*(l-(n-(k-1))+1)%mod;
	return printf("%d",ans),0;
}

C - Election

考虑找一个相对好统计的特征来刻画一个串,首先不难想到将A看作\(1\),将B看作\(-1\),然后求出前缀和数组\(pfx_i\)

不难发现最后\(pfx_i\)\(0\)的地方就是C,否则根据\(pfx_i\)的正负绝对其是A还是B

观察到C的处境很特别,考虑用一个二元组\((pos_i,tp_i)\)来刻画某一个C的特征,其中\(pos_i\)表示其出现的位置,\(tp_i\)表示其之前这一段(到上个C为止是A还是B

不难发现一个串和某个关于C的二元组的集合唯一对应,因此我们不妨用Hash来维护所有的二元组的Hash值,接下来考虑移动第一个字符的位置带来的影响

不妨假设第一个字符初始时就在开头,每次将其往后交换,首先若这次交换的两个字符相同则不会有任何影响

否则若\(s_i=A,s_{i+1}=B\),不难发现交换后带来的影响就是\(pfx_i-=2\);同理若\(s_i=B,s_{i+1}=A\),交换后带来的影响就是\(pfx_i+=2\)

一边枚举一边动态维护整个序列的Hash值即可

#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=1e6+5;
int n,pfx[N]; char s[N];
const int mod1=998244353,mod2=1e9+7;
struct Hasher
{
	int x,y;
	inline Hasher(CI X=0,CI Y=0)
	{
		x=X; y=Y;
	}
	inline LL get_val(void)
	{
		return ((1LL*x)<<31LL)|(1LL*y);
	}
	friend inline bool operator == (const Hasher& A,const Hasher& B)
	{
		return A.x==B.x&&A.y==B.y;
	}
	friend inline Hasher operator + (const Hasher& A,const Hasher& B)
	{
		return Hasher((A.x+B.x)%mod1,(A.y+B.y)%mod2);
	}
	friend inline Hasher operator - (const Hasher& A,const Hasher& B)
	{
		return Hasher((A.x-B.x+mod1)%mod1,(A.y-B.y+mod2)%mod2);
	}
	friend inline Hasher operator * (const Hasher& A,const Hasher& B)
	{
		return Hasher(1LL*A.x*B.x%mod1,1LL*A.y*B.y%mod2);
	}
}pw[N]; unordered_set <LL> rst;
const Hasher seed=Hasher(31,131);
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%s",&n,s+1),pw[0]=Hasher(1,1),i=1;i<=n;++i)
	pw[i]=pw[i-1]*seed,pfx[i]=pfx[i-1]+(s[i]=='A'?1:-1);
	Hasher cur; for (i=1;i<=n;++i) if (pfx[i]==0) cur=cur+pw[i]*(pfx[i-1]>0?Hasher(1,1):Hasher(2,2));
	for (rst.insert(cur.get_val()),i=1;i<n;++i)
	{
		if (s[i]==s[i+1]) continue;
		if (s[i]=='A'&&s[i+1]=='B')
		{
			if (pfx[i]==0) cur=cur-pw[i]*(pfx[i-1]>0?Hasher(1,1):Hasher(2,2));
			if (pfx[i+1]==0) cur=cur-pw[i+1]*(pfx[i]>0?Hasher(1,1):Hasher(2,2));
			if ((pfx[i]-=2)==0) cur=cur+pw[i]*(pfx[i-1]>0?Hasher(1,1):Hasher(2,2));
			if (pfx[i+1]==0) cur=cur+pw[i+1]*(pfx[i]>0?Hasher(1,1):Hasher(2,2));
		} else
		{
			if (pfx[i]==0) cur=cur-pw[i]*(pfx[i-1]>0?Hasher(1,1):Hasher(2,2));
			if (pfx[i+1]==0) cur=cur-pw[i+1]*(pfx[i]>0?Hasher(1,1):Hasher(2,2));
			if ((pfx[i]+=2)==0) cur=cur+pw[i]*(pfx[i-1]>0?Hasher(1,1):Hasher(2,2));
			if (pfx[i+1]==0) cur=cur+pw[i+1]*(pfx[i]>0?Hasher(1,1):Hasher(2,2));
		}
		swap(s[i],s[i+1]); rst.insert(cur.get_val());
	}
	return printf("%d",rst.size()),0;
}

D - Distance Ranking

这题比赛场上过的贼少(比E少了一个数量级),后面想了半天也想不出个所以然,然后看了题解发现我是个傻逼

首先考虑构造一个所有点对间距离全相等的Case,这个也很简单,我们设\(V_{i,j}\)表示点\(i\)\(j\)维的坐标,则\(V_{i,j}=[i==j]\times S\)即可满足上述要求,其中\(S\)为任意常数

此时任意两点间距离都是\(2S^2\),考虑如果我们令\(V_{i,j}=c\ \ (i\ne j)\)会有什么影响,即点\(i\)和点\(j\)的距离变为了\(2S^2-2\times cS+c^2\)

由于所有点之间的距离都有\(2S^2\)因此不妨扔掉不管,如果我们令\(S\)为一个极大值(比如\(10^8\)),而\(c\)的数量级相比于\(S\)定的很小(不妨令\(c\in[1,\frac{N(N-1)}{2}]\)),则\(2S^2-2\times cS+c^2\)的值就变得只和\(c\)有关了

因此我们按照题目要求的排名顺序来确定\(V_{i,j}\)的值即可,代码非常简单

(PS:据说由于这题点数很少,还有很多奇怪的随机化乱搞做法,但由于时间所限就没有去尝试了)

#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=25,INF=1e8;
int n,ans[N][N],x,y;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) ans[i][i]=INF;
	for (i=1;i<=n*(n-1)/2;++i) scanf("%d%d",&x,&y),ans[x][y]+=(n*(n-1)/2-i+1);
	for (i=1;i<=n;++i) for (j=1;j<=n;++j) printf("%d%c",ans[i][j]," \n"[j==n]);
	return 0;
}

E - Last 9 Digits

我只能说第一反应是打表找规律,但这题的规律有点小逆天

首先你要搞出以下两个猜想:

  • 当模数为\(10^k\)时,满足\(n^n\equiv x\pmod {10^k}\)的解\(n\)\([1,10^k]\)的范围内有且仅有一个,且它们构成双射关系
  • 当模数为\(10^{k+1}\)时,设满足\(n^n\equiv x\pmod {10^k}\)的在\([1,10^{k}]\)内的解为\(n'\),则满足\(n^n\equiv x\pmod {10^{k+1}}\)的在\([1,10^{k+1}]\)内的解为\(n=n'+t\times 10^k\ \ (t\in [0,9])\)

例如当\(k=3,x=16\)时,有\(514^{514}\equiv1514^{1514}\equiv2514^{2514}\equiv 16\pmod {10^3}\)

上述结论的具体证明可以看官方题解,大致思路就是先暴力验证当\(k=2\)时成立,然后后面用归纳法证明\(k\ge 2\)亦成立

有了这个结论后就很简单了,我们先大力在\([1,100)\)内找出某个数\(n_0\)满足\(n_0^{n_0}\equiv x\pmod {100}\),然后不停往上递推即可

单组数据复杂度为\(100+9^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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,x,pw[10];
inline int quick_pow(int x,int p,int mod,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(void)
{
	pw[0]=1; for (RI i=1;i<=9;++i) pw[i]=pw[i-1]*10;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t),init();t;--t)
	{
		RI i,j; scanf("%d",&x); int lst;
		for (i=1;i<100;++i) if (quick_pow(i,i,100)==x%100) { lst=i; break; }
		for (i=3;i<=9;++i) for (j=0;j<10;++j)
		{
			int tmp=lst+pw[i-1]*j;
			if (quick_pow(tmp,tmp,pw[i])==x%pw[i]) { lst=tmp; break; }
		}
		printf("%d\n",lst);
	}
	return 0;
}

Postscript

ARC还是太玄妙了,我还是滚回去写CF吧

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