CF1335E Three Blocks Palindrome
我们直接讲E2(hard version)。
设子序列用到的两个字符分别为(a), (b),它们的出现次数分别为(x), (y)(这和题面中的定义是相同的)。考虑枚举(a),枚举(x),则可以确定左边第(x)个(a)的位置,记为(l),右边第(x)个(a)的位置,记为(r),我们要做的就是在([l+1,r-1])区间里找出出现次数最多的颜色,作为(b)。
如何以优秀的复杂度实现上述思路?
先枚举(a),然后从大到小枚举所有的(x)。在(x)变化时,也就是从(a)的某一个出现位置跳到下一个出现位置时,我们就暴力扫过去。这样的总复杂度是(O(nm))的((mleq 200))。因为是从大到小枚举的(x),发现中间的区间([l+1,r-1])是在不断扩大的,所以可以顺便维护出每个值的出现次数,以及其中的最大值。
时间复杂度(O(nm))。
参考代码(片段):
const int MAXN=2e5;
int n,arr[MAXN+5],cnt[205];
int main() {
int T;cin>>T;while(T--){
cin>>n;
for(int i=1;i<=n;++i)cin>>arr[i];
int ans=0;
for(int a=1;a<=200;++a){
vector<pii>vec;
vec.pb(mk(0,n+1));
int i=1,j=n;
while(1){
while(i<j&&arr[i]!=a)++i;
while(j>i&&arr[j]!=a)--j;
if(i>=j)break;
vec.pb(mk(i,j));++i;--j;
}
for(int b=1;b<=200;++b)cnt[b]=0;
int mx=0;
for(int i=vec.back().fi+1;i<=vec.back().se-1;++i){
cnt[arr[i]]++;
mx=max(mx,cnt[arr[i]]);
}
i=vec.back().fi;
j=vec.back().se;
ans=max(ans,(SZ(vec)-1)*2+mx);
for(int t=SZ(vec)-2;t>=0;--t){
while(i>vec[t].fi){
cnt[arr[i]]++;
mx=max(mx,cnt[arr[i]]);
--i;
}
while(j<vec[t].se){
cnt[arr[j]]++;
mx=max(mx,cnt[arr[j]]);
++j;
}
ans=max(ans,t*2+mx);
}
}
cout<<ans<<endl;
}
return 0;
}
CF1335F Robots on a Grid
每个格子都会唯一对应一个它接下来要走到的格子。我们把所有格子按(1dots nm)编号。将每个格子看做一个节点,从每个节点向它下一步要走到的节点连一条有向边。因为每个点都有且仅有一条出边,我们得到了一个基环内向树森林。
问题转化为,让你在一棵基环内向树上,选择若干个点放置robot。进程开始后的每个时刻,每个robot会向前走一条边。要求任意时刻不能有两个robot出现在同一个节点上。在此基础上使得放置的robot数量尽可能多。在满足前面所有条件的基础上使得初始时在黑色节点上的robot尽可能多。
在环上选择一个点(x),断掉(x)与(fa[x])之间的边,则基环树变为一棵以(x)为根的树。那么,基环树上,某个节点到(x)的距离,就等于新树上这个节点的深度。我们对新树做一遍dfs求出每个节点的深度。设环长为(len)。则两个节点能同时被放置robot(并且在之后的过程中永远不会撞车),当且仅当它们的深度(mod len)的值不相等。
于是,我们只需要对(0dots len-1)的每个值,求出有没有深度(mod len)等于这个值的节点,有没有黑色节点。
时间复杂度(O(n))。
参考代码(片段):
const int MAXN=1e6;
int n,m,c[MAXN+5],fa[MAXN+5],cnt0[MAXN+5],cnt1[MAXN+5],dep[MAXN+5],mxdep,ans1,ans2;
bool vis[MAXN+5],v1[MAXN+5],v2[MAXN+5];
vector<int>G[MAXN+5];
char str[MAXN+5];
inline int id(int i,int j){return (i-1)*m+j;}
void dfs(int u,int frm,const int& root){
if(!c[u])cnt0[dep[u]]++;else cnt1[dep[u]]++;
mxdep=max(mxdep,dep[u]);
vis[u]=1;
for(int i=0;i<SZ(G[u]);++i){
int v=G[u][i];
if(v==frm||v==root)continue;
dep[v]=dep[u]+1;
dfs(v,u,root);
}
}
int main() {
int T;cin>>T;while(T--){
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>(str+1);
for(int j=1;j<=m;++j){
if(str[j]=='0')c[id(i,j)]=0;
else c[id(i,j)]=1;
}
}
for(int i=1;i<=n;++i){
cin>>(str+1);
for(int j=1;j<=m;++j){
if(str[j]=='U')fa[id(i,j)]=id(i-1,j);
if(str[j]=='R')fa[id(i,j)]=id(i,j+1);
if(str[j]=='D')fa[id(i,j)]=id(i+1,j);
if(str[j]=='L')fa[id(i,j)]=id(i,j-1);
}
}
n*=m;
for(int i=1;i<=n;++i)vis[i]=0,vector<int>().swap(G[i]);
for(int i=1;i<=n;++i){
G[fa[i]].pb(i);
//cout<<"edge "<<i<<" "<<fa[i]<<endl;
}
ans1=0;ans2=0;
for(int i=1;i<=n;++i)if(!vis[i]){
int x=i;
while(!vis[x]){
vis[x]=1;
x=fa[x];
}
// take x as root
//cout<<"root "<<x<<endl;
dep[x]=0;
mxdep=0;
dfs(x,0,x);
int len=dep[fa[x]]+1;
for(int j=0;j<=mxdep;++j){
if(cnt0[j]){
v1[j%len]=1;
v2[j%len]=1;
}
else if(cnt1[j]){
v1[j%len]=1;
}
}
for(int j=0;j<len;++j){
ans1+=v1[j];ans2+=v2[j];
}
//cout<<"** "<<ans1<<" "<<ans2<<endl;
for(int j=0;j<=mxdep;++j){
cnt0[j]=cnt1[j]=0;v1[j]=v2[j]=0;
}
}
cout<<ans1<<" "<<ans2<<endl;
}
return 0;
}