Atcoder Beginner Contest 339 解题报告

Atcoder Beginner Contest 339

场评:B>C,D>E,F>G,中国选手最擅长的 G,集体上分

A - TLD

Simulate.

string s;
void Solve()
{
	char c;
	while(cin>>c)
	{
		if(c=='.')s="";
		else s+=c;
	}
	cout<<s;
}

B - Langton's Takahashi

Simulate.

const int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int n,m,k;
bool f[100][100];
void Solve()
{
	cin>>n>>m>>k;
	int cx=0,cy=0;unsigned d=0;
	while(k--)
	{
		if(f[cx][cy])d=(d-1)&3;
		else d=(d+1)&3;
		f[cx][cy]^=1;
		cx=(cx+dx[d]+n)%n,cy=(cy+dy[d]+m)%m;
	}
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)cout<<(f[i][j]?'#':'.');
		cout<<endl;
	}
}

C - Perfect Bus

从后往前累加然后取 \(\max\) 即可。时间 \(\Theta(n)\)

int n,a[200005];
ll mx,cur;
void Solve()
{
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=n;i;i--)
		mx=max(mx,cur+=a[i]);
	cout<<mx;
}

D - Synchronized Players

设两人位置分别为 \((x_1,y_1),(x_2,y_2)\),则以 \((x_1,y_1,x_2,y_2)\) 为状态 BFS 即可。

时间 \(\Theta(n^4)\)

const int N=60,dx[4]={-1,0,0,1},dy[4]={0,-1,1,0};
int n;
string s[N];
int f[N][N][N][N];
int p1x=-1,p1y,p2x,p2y;
void Solve()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>s[i];
		for(int j=0;j<n;j++)
			if(s[i][j]=='P')
			{
				if(~p1x)p2x=i,p2y=j;
				else p1x=i,p1y=j;
			}
	}
	memset(f,-1,sizeof(f));
	queue<array<int,4>>q;
	q.push({p1x,p1y,p2x,p2y});
	f[p1x][p1y][p2x][p2y]=0;
	while(nempty(q))
	{
		auto x=q.front();q.pop();
		int x1=x[0],y1=x[1],x2=x[2],y2=x[3];
		if(x1==x2&&y1==y2)return (void)(cout<<f[x1][y1][x2][y2]);
		for(int i=0;i<4;i++)
		{
			int nx1=x1+dx[i],ny1=y1+dy[i],nx2=x2+dx[i],ny2=y2+dy[i];
			if(nx1<0||ny1<0||nx1>=n||ny1>=n||s[nx1][ny1]=='#')
				nx1=x1,ny1=y1;
			if(nx2<0||ny2<0||nx2>=n||ny2>=n||s[nx2][ny2]=='#')
				nx2=x2,ny2=y2;
			if(!~f[nx1][ny1][nx2][ny2])
				f[nx1][ny1][nx2][ny2]=f[x1][y1][x2][y2]+1,
				q.push({nx1,ny1,nx2,ny2});
		}
	}
	cout<<-1;
}

E - Smooth Subsequence

\(dp_i\) 表示某时刻最后一个数为 \(i\) 时的最大长度。

加入一个数 \(x\) 时,则令

\[dp_x\gets1+\max_{i=x-d}^{x+d}(dp_i) \]

即可。单点改区间 \(\max\),线段树解决。时间 \(\Theta(n\log n)\)

const int N=500005,P=N-5;
int n,d;
struct SGT
{
	int tr[1<<20];
#define lid (id<<1)
#define rid (lid|1)
#define mid (l+r>>1)
#define rmd (mid+1)
	SGT(){memset(tr,0,sizeof(tr));}
	void pushup(int id){tr[id]=max(tr[lid],tr[rid]);}
	void modify(int pos,int val,int l=1,int r=P,int id=1)
	{
		if(l==r)return (void)(tr[id]=val);
		if(pos<=mid)modify(pos,val,l,mid,lid);
		else modify(pos,val,rmd,r,rid);
		pushup(id);
	}
	int mx(int ql,int qr,int l=1,int r=P,int id=1)
	{
		if(ql==l&&qr==r)return tr[id];
		int rs=0;
		if(ql<=mid)rs=max(rs,mx(ql,min(qr,mid),l,mid,lid));
		if(qr>=rmd)rs=max(rs,mx(max(ql,rmd),qr,rmd,r,rid));
		return rs;
	}
}t;
void Solve()
{
	cin>>n>>d;
	for(int i=1,x;i<=n;i++)
	{
		cin>>x;
		t.modify(x,1+t.mx(max(1,x-d),min(P,x+d)));
	}
	cout<<t.mx(1,P);
}

F - Product Equality

先想如果 \(a_i\le10^9\) 怎么做。直接枚举 \(i,j\) 然后算出 \(k\) 共有几个即可。时间 \(\Theta(n^2)\)

然后想 \(a_i<10^{1000}=10^L\) 时怎么做。此时做一次乘法都要 \(\Theta(L\log L)\)而且 ABC 的 F 是基本不会考 FFT 的,直接乘必 T 无疑。

考虑 hash,随便取几个模数 \(p\)(假设为 \(s\) 个),那么可以很轻松的算出每个 \(a_i\)\(p\) 取模后的值。然后判一下模意义下成立的三元组,取个交集即可。

错误率约为 \(\frac1{\prod p}\),时间 \(\Theta(n(n+L)s)\)

下列代码取了 4 个固定模数和 6 个随机模数。

mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
uniform_int_distribution<int>f(1000000000,2000000000);
const int N=1005;
ll md[10]={998244353,1000000007,1000000009,2147483647};
int n;
array<ll,10> v[N];
map<array<ll,10>,int>st;
void Solve()
{
	for(int i=4;i<=9;i++)
		md[i]=f(rnd);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		string s;cin>>s;
		for(char j:s)
			for(int k=0;k<10;k++)
				v[i][k]=(v[i][k]*10+j-'0')%md[k];
		st[v[i]]++;
	}
	int cnt=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			array<ll,10>c;
			for(int k=0;k<10;k++)c[k]=v[i][k]*v[j][k]%md[k];
			cnt+=st[c];
		}
	cout<<cnt;
}

G - Smaller Sum

一眼扫描线秒了,但是一看强制在线。/doge

(主席树也可以,而且更优秀(\(\Theta(n\log n)\)),这是赛时做法。)

考虑对序列分块,块长为 \(B\),令 \(val_i\) 为前 \(i\) 个块中的所有元素从小到大排序后的结果。

预处理 \(val\) 时,先给序列排序,然后从小到大对每个元素,把其所在块及之后的块全部 push_back 这个元素即可。此部分时间 \(\Theta(n\cdot\frac nB)=\Theta(\frac{n^2}B)\)

先把询问差分成前缀中 \(\le x\) 的和。

然后前面的整块可以直接 upper_bound 求出 \(\le x\) 的分界,然后开始时预处理前缀和即可求出其和。后面的散块直接暴力 check 是否 \(\le x\) 即可。此部分时间 \(\Theta(\log\frac nB+B)\)

总时间复杂度 \(\Theta(\frac{n^2}B+qB+q\log\frac nB)\)。当 \(n,q\) 同阶时取 \(B=\sqrt n\) 即可做到 \(\Theta(n\sqrt n)\) 时空复杂度。

注意空间消耗别重蹈我 PKUWC2024D2T3 的覆辙(谁能想到几天前才被卡分块的我几天后又来写分块了呢)

const int N=200005,B=450;
int n,q,a[N],ps[N];
VI vals[B];
vector<ll> sums[B];
int K(int x){return (x+B-1)/B;}
ll get(int r,int x)
{
	int k=K(r+1);
	ll res=0;
	int po=upper_bound(ALL(vals[k-1]),x)-vals[k-1].begin()-1;
	if(~po)res+=sums[k-1][po];
	for(int i=(k-1)*B+1;i<=r;i++)
		res+=a[i]*(a[i]<=x);
	return res;
}
void Solve()
{
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	iota(ps+1,ps+n+1,1);
	sort(ps+1,ps+n+1,[&](int x,int y){return a[x]<a[y];});
	for(int i=1;i<=n;i++)
	{
		int p=ps[i];
		for(int j=K(p);j<B;j++)vals[j].PB(a[p]);
	}
	for(int i=1;i<B;i++)
	{
		ll sum=0;
		for(int j:vals[i])sums[i].PB(sum+=j);
	}
	cin>>q;
	ll ans=0;
	while(q--)
	{
		ll l,r,x;
		cin>>l>>r>>x;
		l^=ans,r^=ans,x^=ans;
		cout<<(ans=get(r,x)-get(l-1,x))<<endl;
	}
posted @ 2024-02-03 22:31  No_Play_Yes_Splay  阅读(104)  评论(0编辑  收藏  举报