CodeTON Round 3 (Div. 1 + Div. 2, Rated, Prizes!)
Preface
早自习后的两节空课不想回寝室了就在教室补题,发现没睡醒的时候真是究极混沌状态
憋了半天勉强写了ABC,D是后来晚自习补上的,E看错题目了,F不会看题解会的,GH不打算看
A. Indirect Sort
找性质题,不难发现若\(a_1=1\),则我们可以对\([2,n]\)中的元素进行交换操作,肯定可以做到有序
否则若\(a_1\ne 1\),则我们不能把在后面的\(1\)变大或是交换到\(a_1\)的位置上,因此肯定不合法
#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=15;
int t,n,a[N];
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]);
puts(a[1]==1?"Yes":"No");
}
return 0;
}
B. Maximum Substring
感觉思维难度比T1还低啊
不难发现若\(x>0\and y>0\)的话只要考虑整个串即可,否则找出最长的连续颜色段,比较一下即可
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,c[2]; char s[N];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d%s",&n,s+1),c[0]=c[1]=0,i=1;i<=n;++i) ++c[s[i]-'0'];
int mx=0,lst=1; for (i=2;i<=n;++i) if (s[i]!=s[i-1]) mx=max(mx,i-lst),lst=i;
mx=max(mx,n-lst+1); printf("%lld\n",max(1LL*mx*mx,1LL*c[0]*c[1]));
}
return 0;
}
C. Complementary XOR
我发现只要不是比赛的时候我猜结论就准的一批,真到比赛的时候屁都猜不出来
刚开始想了半天的构造前后缀的方法,但是不能保证操作次数小于\(n+5\)
后来随便Rush了一发直接把\(a\)先全部置为\(0\)然后看\(b\)是不是全为\(0\)或\(1\),没想到直接过了
其实原理很简单,我们考虑倒着做,从初始两个串都是\(0\)开始做变换
不难发现奇数次操作后\(a_i,b_i\)一定不同,而偶数次操作后\(a_i,b_i\)一定相同
因此只有初始时所有\(a_i,b_i\)都不同或者都相同才有解,有解的情况一定可以用上面的方法求出一组解
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,l[N],r[N],cnt,dlt[N]; char a[N],b[N];
inline void flip(CI L,CI R)
{
if (L>R) return; ++dlt[L]; --dlt[R+1];
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i,j; for (scanf("%d%s%s",&n,a+1,b+1),i=0;i<=n+1;++i) dlt[i]=0;
for (cnt=0,i=1;i<=n;++i) if (a[i]=='1')
{
for (j=i;j<=n&&a[j]=='1';++j);
l[++cnt]=i; r[cnt]=j-1; flip(1,i-1); flip(j,n); i=j;
}
for (i=1;i<=n;++i) if ((dlt[i]+=dlt[i-1])&1) b[i]=b[i]=='0'?'1':'0';
bool flag=1; for (i=2;i<=n&&flag;++i) if (b[i]!=b[1]) flag=0;
puts(flag?"YES":"NO"); if (!flag) continue;
if (b[1]=='1') l[cnt+1]=1,r[cnt+1]=n,l[cnt+2]=r[cnt+2]=1,l[cnt+3]=2,r[cnt+3]=n,cnt+=3;
for (printf("%d\n",cnt),i=1;i<=cnt;++i) printf("%d %d\n",l[i],r[i]);
}
return 0;
}
D. Count GCD
额很有CF风格的一道题吧,又是码个暴力然后分析复杂度的题
首先我们发现每个位置的情况独立,求出每一位的情况然后乘起来即可
由于\(\gcd(b_1,b_2,\cdots,b_{i-1})=a_{i-1}\),因此要求的其实就是\(\gcd(a_{i-1},b_i)=a_i\)的方案数
先去除无解的情况,稍加转化就是\(\gcd(\frac{a_{i-1}}{a_i},\frac{b_i}{a_i})=1\)的方案数,即在\([1,\lfloor\frac{m}{a_i}\rfloor]\)中与\(\frac{a_{i-1}}{a_i}\)互质的数的个数
这个其实是个经典问题了,我们把\(\frac{a_{i-1}}{a_i}\)质因数分解,然后用容斥算方案数即可
复杂度看似爆炸其实由于\(10^9\)范围内质因数分解出最多只有\(10\)个,因此这部分复杂度是\(2^{10}\)级别的
同时我们观察到在有解的情况下\(a_i\)一定是单调不增的,且若\(a_i\ne a_{i-1}\),则\(a_i\)在除去一个因子后衰减的次数是\(\log a_i\)量级的
当\(a_{i-1}=a_i\)的情形直接用map
顺便记录下即可,复杂度大概是\(O(n\log a_i\times 2^{10}\times 10)\)
#include<cstdio>
#include<iostream>
#include<map>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,mod=998244353;
int t,n,m,a[N],ans; map <int,int> rst; vector <int> p,rp;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i,j,k; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&a[i]);
p.clear(); rst.clear(); int t=a[1];
for (i=2;i*i<=t;++i) if (t%i==0)
{
p.push_back(i); while (t%i==0) t/=i;
}
if (t>1) p.push_back(t); for (ans=1,i=2;i<=n;++i)
{
if (a[i-1]%a[i]) { ans=0; break; }
if (a[i-1]==a[i]&&rst.count(a[i])) { ans=1LL*ans*rst[a[i]]%mod; continue; }
int lim=m/a[i],t=a[i-1]/a[i]; rp.clear();
for (int x:p) if (t%x==0) rp.push_back(x);
int ret=0; for (j=0;j<(1<<rp.size());++j)
{
int cnt=0,mul=1; for (k=0;k<rp.size();++k)
if ((j>>k)&1) ++cnt,mul*=rp[k];
ret+=(cnt&1?-1:1)*lim/mul;
}
ans=1LL*ans*ret%mod; if (a[i-1]==a[i]) rst[a[i]]=ret;
}
printf("%d\n",ans);
}
return 0;
}
E. Bracket Cost
刚开始的时候看错题目了,以为那个移位操作只能对整个串进行操作,没想到是对子串
首先一个经典的trick,把括号序列的前缀和\(pfx\)求出来,左括号看作1,右括号看作-1
然后考虑一个区间\([l,r]\)的答案,不难发现首先要把左右括号的个数补成一样
接下来考虑位移操作,不难发现我们都是可以把位于最后的左括号换到最前面
因此整理一下答案就是\(\max(f_{l-1},f_r)-\min_{l-1\le i\le r} f_i\),不难发现两边分开计算比较简单
前面的用树状数组维护下,后面的用单调栈维护下即可
#include<cstdio>
#define RI register int
#define CI const int&
const int N=200005;
int t,n,lim,pfx[N],L[N],R[N],stk[N],top; char s[N]; long long ans;
#define lowbit(x) (x&-x)
class TreeArray1 //count the number of x that x<=i
{
private:
int bit[N<<1];
public:
inline void init(CI n)
{
for (RI i=1;i<=n;++i) bit[i]=0;
}
inline void add(RI x)
{
for (;x<=lim;x+=lowbit(x)) ++bit[x];
}
inline int get(RI x,int ret=0)
{
for (;x;x-=lowbit(x)) ret+=bit[x]; return ret;
}
}T1;
class TreeArray2 //count the sum of x that x>=i
{
private:
long long bit[N<<1];
public:
inline void init(CI n)
{
for (RI i=1;i<=n;++i) bit[i]=0;
}
inline void add(RI x,CI y)
{
for (;x;x-=lowbit(x)) bit[x]+=y;
}
inline long long get(RI x,long long ret=0)
{
for (;x<=lim;x+=lowbit(x)) ret+=bit[x]; return ret;
}
}T2;
#undef lowbit
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; scanf("%d%s",&n,s+1); ans=0; lim=n+n+1; T1.init(lim); T2.init(lim);
for (pfx[0]=0,i=1;i<=n;++i) pfx[i]=pfx[i-1]+(s[i]=='('?1:-1);
for (i=0;i<=n;++i) pfx[i]+=n+1; for (i=0;i<=n;++i)
ans+=1LL*T1.get(pfx[i])*pfx[i]+T2.get(pfx[i]+1),T1.add(pfx[i]),T2.add(pfx[i],pfx[i]);
for (stk[top=i=0]=-1;i<=n;++i)
{
while (top&&pfx[stk[top]]>pfx[i]) --top;
L[i]=stk[top]; stk[++top]=i;
}
for (stk[top=0]=n+1,i=n;~i;--i)
{
while (top&&pfx[stk[top]]>=pfx[i]) --top;
R[i]=stk[top]; stk[++top]=i;
}
for (i=0;i<=n;++i) ans-=1LL*pfx[i]*(1LL*(i-L[i])*(R[i]-i)-1);
printf("%lld\n",ans);
}
return 0;
}
F. Majority
STO CXR ORZ
妈的看了半天Tutorial发现陈指导写了这题的博客,赶紧去膜一发
首先我们发现对于任意一个串,我们发现在操作了一定次数后它能变成的状态一定是唯一的
考虑若这个串不是全\(1\),且假设两个端点都不是\(0\),那么我们发现这个串一定变成了\(2k+1\)个连续颜色段
其中奇数下标的段都是\(1\),偶数下标的段都是\(0\),同时每一个\(0\)段左右两侧的\(1\)段的长度之和均小于这个\(0\)段
我们不妨这样设计状态,令\(f_{i,j}\)表示长度为\(i\)的串,最终状态下结尾的\(1\)的长度为\(j\)的方案数
不难发现只有\(f_{i,i}\)是合法状态,而\(f_{i,j}(j<i)\)都是不合法的,而且由容斥我们有\(f_{i,i}=2^{i-2}-\sum_{j=1}^{i-1} f_{i,j}\)(特别地,\(f_{1,1}=1\))
考虑对于\(j<i\)的\(f_{i,j}\)的转移,考虑枚举前面一段\(0\)的长度\(k\),设再前面一段\(1\)的长度为\(x\),则有:
令\(g_{t}=\sum_{i+j\le t} f_{i,j}\),则可以\(O(1)\)转移,总复杂度\(O(n^2)\)
#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=5005;
int n,mod,f[N][N],g[N],pw;
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j; for (scanf("%d%d",&n,&mod),i=2;i<=n;++i) g[i]=1;
for (f[1][1]=pw=1,i=2;i<=n;++i)
{
for (j=1;j<i;++j) if (i-2*j>0) f[i][j]=1LL*g[i-2*j-1]*f[j][j]%mod;
for (f[i][i]=pw,inc(pw,pw),j=1;j<i;++j) inc(f[i][i],mod-f[i][j]);
int sum=0; for (j=1;i+j<=n;++j) inc(sum,f[i][j]),inc(g[i+j],sum);
}
return printf("%d",f[n][n]),0;
}
Postscript
妈的补题的时候写什么都是一遍过,比赛的时候写什么都要挂