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\) 时,则令
即可。单点改区间 \(\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;
}