ZJC比赛
\(\large{Updatete}\)
先放张图 😇😇😇
这次是真的没想再改了,但是一到教室就又会怎么优化了
没必要每个点都让它和其他所有点判断一下,可以从上一个点加加减减啥的转过来
然后我就在昨天那个 \(n*n\) 代码的基础上,只让它把第一个点的值求出来,剩下用优化的思想从上一个点转移再加加减减确定最优值
点击查看答辩
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int inf=1e8;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
int main()
{
cin>>T;
while(T--)
{
scanf("%s",c);
len=strlen(c);
for(int i=len;i>=1;i--)
c[i]=c[i-1];
tb=c[0]=0;
for(int i=1;i<=len;i++)
if(c[i]=='R')
b[++tb]=i;
for(int i=tb+1;i<=tb*2;i++)
b[i]=b[i-tb]+len;
for(int i=1;i<=tb*2;i++)
q[i]=q[i-1]+b[i]-b[i-1]-1;
int res=0,cnt=0,zz=1;
while((cnt<res&&zz+1<=1+tb)||!res)
{
zz++;
if(cnt)
res=cnt;
cnt=0;
for(int j=2;j<zz;j++)
cnt+=q[j]-q[1];
for(int j=zz;j<1+tb;j++)
cnt+=q[1+tb]-q[j];
}
zz--;
ans=res;
int l=zz,r=tb-zz;
for(int i=2;i<=tb;i++)
{
res+=r*(q[zz+r+1]-q[zz+r])-(l-1)*(q[zz-l+2]-q[zz-l+1]);
l--,r++;
while(res-q[zz+r]+q[zz+1]+q[zz+1]-q[zz-l+1]<=res)
{
res=res-q[zz+r]+q[zz+1]+q[zz+1]-q[zz-l+1];
zz++,l++,r--;
}
//cout<<ans<<" "<<res<<" "<<l<<" "<<r<<"---\n";
ans=min(ans,res);
}
printf("%d\n",ans);
}
return 0;
}
发现一个点也过不了
再发现循环 i 次, i 一次也没用上,然后就看到其实 $zz-l+1==i $ $ zz+l=i+tb$
也就是 l,r 不仅啥也没优化到反而增加了思维复杂度 😅
但是并没有什么质的改变,所以还是过不了
再看到自己昨天写的代码以 zz 作为分割点,i~zz-1 是一部分,zz~i+tb 是一部分
但是今天写的东西是建立在 zz 属于第一部分上的
改完就过了,,,
点击查看答辩
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int inf=1e8;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
int main()
{
cin>>T;
while(T--)
{
scanf("%s",c);
len=strlen(c);
for(int i=len;i>=1;i--)
c[i]=c[i-1];
tb=c[0]=0;
for(int i=1;i<=len;i++)
if(c[i]=='R')
b[++tb]=i;
for(int i=tb+1;i<=tb*2;i++)
b[i]=b[i-tb]+len;
q[1]=0;
for(int i=2;i<=tb*2;i++)
{
q[i]=q[i-1]+b[i]-b[i-1]-1;
// cout<<q[i]<<' ';
}
// cout<<endl;
int res=0,cnt=0,zz=1;
while((cnt<res&&zz<=tb)||!res)
{
zz++;
if(cnt)
res=cnt;
cnt=0;
for(int j=2;j<=zz;j++)
cnt+=q[j]-q[1];
for(int j=zz+1;j<1+tb;j++)
cnt+=q[1+tb]-q[j];
}
zz--;
ans=res;
// cout<<ans<<" "<<res<<" "<<zz<<"---\n";
for(int i=2;i<=tb;i++)
{
res+=(i+tb-zz-1)*(q[i+tb]-q[i+tb-1]);
res-=(zz-i+1)*(q[i]-q[i-1]);
while(res-q[i+tb]+q[zz+1]+q[zz+1]-q[i]<res&&zz<i+tb)
{
res=res-q[i+tb]+q[zz+1]+q[zz+1]-q[i];
zz++;
}
ans=min(ans,res);
}
printf("%d\n",ans);
}
return 0;
}
又是就过了 \(50pts\)
莫名其妙,真是让人摸不着头发 😅
再再再再然后,发现没必要从第一个点的最优解开始转移
也就是说就不该留昨天的代码,哪怕只对一个点求也会 T 掉
删了就对了
,当然把最初始的状态加上,也不用管他是不是最优的 总不能转移空气吧
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+10;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
signed main()
{
cin>>T;
while(T--)
{
scanf("%s",c);
len=strlen(c);
for(int i=len;i>=1;i--)
c[i]=c[i-1];
tb=c[0]=0;
for(int i=1;i<=len;i++)
if(c[i]=='R')
b[++tb]=i;
for(int i=tb+1;i<=tb*2;i++)
b[i]=b[i-tb]+len;
q[1]=0;
for(int i=2;i<=tb*2;i++)
q[i]=q[i-1]+b[i]-b[i-1]-1;
int res=0,zz=1;
for(int i=2;i<=tb;i++)
res+=q[tb+1]-q[i];
ans=res;
for(int i=1;i<=tb;i++)
{
if(i!=1)
{
res+=(i+tb-zz-1)*(q[i+tb]-q[i+tb-1]);
res-=(zz-i+1)*(q[i]-q[i-1]);
}
while(res-q[i+tb]+q[zz+1]+q[zz+1]-q[i]<res&&zz<i+tb)
{
res=res-q[i+tb]+q[zz+1]+q[zz+1]-q[i];
zz++;
}
ans=min(ans,res);
}
printf("%lld\n",ans);
}
return 0;
}
😇😇😇
小插曲
Huge:我觉得你们最近学习状态不错,找我调代码的都少了...
某人:老师有空帮忙看看我的代码呗
Huge:...调代码这件事吧,还得看你们自己,总不能考场上也让别人帮你调吧
(显然没有成功)
————————————————————————————————————————————————————————————————————————————————————————————————————————
\(\large{Uptated}\)
本来没有继续写t3的欲望了,但是昨天DZ说他改出来了,然后我就去再看了眼题解
当时想的是之前因为和题解思路不太一样,所以没仔细看,这回看到了关键字:二分、单调指针之类的
但是并不知道我的思路怎么二分
然后晚上回去想了5分钟就知道我赛时代码哪里假了
转化成链后,应该在两个点(相同的点)之间的剩下 \(n-1\) 个点上取离那两个点近的那一个
然后 二分||指针 的思路也就有了:
因为肯定是一边全向一边转,另一边相反,且下一个分割点貌似一定在这一个的右边
所以说 二分||指针 出那个分割点就好
顺便再想了会 Tarjan 的有向图,发现现在只会强连通分量了,想无向图想不出来刚打算睡觉的时候,被同学的呼噜轰炸了半个小时才睡着
今天本来上午最后一节公自就该来机房实现一下思路的,但是有俩山大的学长来宣传学校了,讲完已经上课了也没好意思再往外走
下午来机房后先花了3分钟改进了一下之前的代码,也就是对每一个点,只在链上同一个点中间计算离哪个近,发现能跑 \(50pts\) (记住这个数)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
int main()
{
cin>>T;
while(T--)
{
scanf("%s",c);
len=strlen(c);
for(int i=len;i>=1;i--)
c[i]=c[i-1];
tb=c[0]=0;
for(int i=1;i<=len;i++)
if(c[i]=='R')
b[++tb]=i;
for(int i=tb+1;i<=tb*2;i++)
b[i]=b[i-tb]+len;
for(int i=1;i<=tb*2;i++)
q[i]=q[i-1]+b[i]-b[i-1]-1;
ans=N<<5;
for(int i=1;i<=tb;i++)
{
int res=0;
for(int j=i+1;j<i+tb;j++)
res+=min(q[j]-q[i],q[i+tb]-q[j]);
ans=min(ans,res);
}
printf("%d\n",ans);
}
return 0;
}
然后就开始写优化
试了一下二分发现不太适合在这里用
然后就敲了一节课的单调指针,说实话真没想到能用这么久
反正最后过了,,,
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int inf=1e9;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
int main()
{
cin>>T;
while(T--)
{
scanf("%s",c);
len=strlen(c);
for(int i=len;i>=1;i--)
c[i]=c[i-1];
tb=c[0]=0;
for(int i=1;i<=len;i++)
if(c[i]=='R')
b[++tb]=i;
for(int i=tb+1;i<=tb*2;i++)
b[i]=b[i-tb]+len;
for(int i=1;i<=tb*2;i++)
q[i]=q[i-1]+b[i]-b[i-1]-1;
ans=inf;
int res,cnt,zz=1;
for(int i=1;i<=tb;i++)
{
cnt=res=0;
while((cnt<res&&zz+1<=i+tb)||!res)
{
zz++;
if(cnt)
res=cnt;
cnt=0;
for(int j=i+1;j<zz;j++)
cnt+=q[j]-q[i];
for(int j=zz;j<i+tb;j++)
cnt+=q[i+tb]-q[j];
}
zz-=2;
ans=min(ans,res);
}
printf("%d\n",ans);
}
return 0;
}
\(45pts\)
那正确性肯定没问题啊,问题出在哪呢
可以发现纠正完后的赛时代码时间复杂度是 \(n*n\) 的
但是我优化了一个单调指针之后,就成 \(n*n*log(n)\) 的了
最会优化的一集 😅
咋最近这么能浪费时间啊 😇😇😇
欸,好像想到咋优化了 (逃
事实证明并没有,哪位大佬知道我这思路咋优化,求教教
————————————————————————————————————————————————————————————————————————————————————————————————————————
昨天 \(Huge\) 说要给信奥的考场试,我以为我们仨不用考来着,一来机房,打开OJ,哦,ZJC比赛,妙啊,(话说为啥把我放在最后一个,虽然最后我考的也最烂就是了,下次一定要让他把我放在第一个
T1
题意:给一个字符串,第二个字符串是前一个字符串的前一半 + 一个字符,问 1 的前缀和 2 的后缀最多相等的个数
显然 \(KMP\) 板子题,,,但是我忘了
但是显然 \(hash\) 也能做,但是我忘了咋求字串的hash值了
那么怎么办呢
我把前一个字符串的所有可能情况都求了个hash值,甚至还没有直接一个一个比对跑得快
(因为答案最多就是第二个字符串的长度也就是第一个的一半)
,大概是因为kmp没打出来,然后急了
😅
赛时代码 64pts
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int base=13331;
unsigned long long a[N],b[N];
int T,la,lb;
char ca[N],cb[N];
inline void Hash()
{
unsigned long long res;
for(int i=1;i<=la;i++)
{
res=0;
for(int j=i;j>=1;j--)
res=res*base+ca[j];
a[i]=res;
}
for(int i=1,j=lb;j>=1;i++,j--)
b[i]=b[i-1]*base+cb[j];
return ;
}
int main()
{
cin>>T;
while(T--)
{
scanf("%d%d",&la,&lb);
scanf("%s",ca+1);
for(int i=1;i<=lb;i++)
cb[i]=ca[i];
scanf("%c",&cb[++lb]);
while(cb[lb]<'a'||cb[lb]>'z')
scanf("%c",&cb[lb]);
Hash();
for(int i=lb;i>=0;i--)
{
if(a[i]==b[i])
{
printf("%d\n",i);
break;
}
}
}
return 0;
}
赛后代码 100pts
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int base=13331;
unsigned long long a[N],b[N],p[N];
int T,la,lb;
char ca[N],cb[N];
inline void Hash()
{
unsigned long long res;
for(int i=1;i<=lb;i++)
{
a[i]=a[i-1]*base+ca[i];
b[i]=b[i-1]*base+cb[i];
}
return ;
}
int main()
{
p[0]=1;
for(int i=1;i<=N;i++)
p[i]=p[i-1]*base;
cin>>T;
while(T--)
{
scanf("%d%d",&la,&lb);
scanf("%s",ca+1);
for(int i=1;i<=lb;i++)
cb[i]=ca[i];
scanf("%c",&cb[++lb]);
while(cb[lb]<'a'||cb[lb]>'z')
scanf("%c",&cb[lb]);
Hash();
int f=1;
for(int i=1,j=lb;i<=lb;i++,j--)
{
if(a[j]==b[lb]-b[i-1]*p[lb-i+1])
{
f=0;
printf("%d\n",j);
break;
}
}
if(f)
printf("0\n");
}
return 0;
}
/*
2
5 3
adabc
d
6 6
aaaaaa
a
*/
T2
题意:\(Tarjan\) 求割点的板子
老师预料到我还记得tarjan咋敲,但是他没想到我根本没写无向tarjan的题
不过好的一点是我前两天看了dij求最小环的做法(我为啥要看这?),大概有 \(30pts\) 的思路,就是把每条边删了,再从1 \(dfs\) 到n 看看走不走得通
然后就出现了一点问题:
图是双向的,但是我单向删的边,所以还没图保证为树的特殊性质分高
如果你还感觉不太对劲的话,大概是我脑子抽了不知道为啥求割点要删边去判,反例随便就能举出来
最后特殊性质也忘了打了 😅
赛时代码 5pts
#include<bits/stdc++.h>
using namespace std;
const int N=4e6+10;
int T,n,m,tot,cnt,res,ans[N];
int head[N],nxt[N<<1],to[N<<1];
bool flag[N],f;
inline void add(int u,int v)
{
nxt[++tot]=head[u];
head[u]=tot;
to[tot]=v;
return ;
}
inline void dfs(int x)
{
flag[x]=true;
if(x==n)
f=1;
if(f)
return ;
int y;
for(int i=head[x];i;i=nxt[i])
{
y=to[i];
if(!flag[y])
dfs(y);
}
return ;
}
int main()
{
cin>>T;
while(T--)
{
for(int i=1;i<=n;i++)
head[i]=0;
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
int ne,t;
for(int i=2;i<n;i++)
{
for(int j=head[i];j;j=nxt[j])
{
ne=nxt[j],t=to[j];
nxt[j]=to[j]=0;
f=0;
dfs(1);
nxt[j]=ne,to[j]=t;
if(!f)
{
if(!ans[i])
res++;
if(!ans[t]&&t!=n&&t!=1)
res++;
ans[i]=ans[t]=1;
}
for(int k=1;k<=n;k++)
flag[k]=false;
}
}
printf("%d\n",res);
res=0;
for(int i=2;i<n;i++)
if(ans[i])
{
ans[i]=0;
printf("%d ",i);
}
ans[1]=ans[n]=0;
printf("\n");
}
return 0;
}
赛后代码 30pts(tarjan改天再说,特殊性质懒得打了)
#include<bits/stdc++.h>
using namespace std;
const int N=4e6+10;
int T,n,m,tot,cnt,res,ans[N];
int head[N],nxt[N<<1],to[N<<1];
bool flag[N],f;
inline void add(int u,int v)
{
nxt[++tot]=head[u];
head[u]=tot;
to[tot]=v;
return ;
}
inline void dfs(int x)
{
flag[x]=true;
if(x==n)
f=1;
if(f)
return ;
int y;
for(int i=head[x];i;i=nxt[i])
{
y=to[i];
if(!flag[y])
dfs(y);
}
return ;
}
int main()
{
cin>>T;
while(T--)
{
for(int i=1;i<=n;i++)
head[i]=0;
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
for(int i=2;i<n;i++)
{
flag[i]=1;
f=0;
dfs(1);
if(!f)
{
res++;
ans[i]=1;
}
for(int j=1;j<=n;j++)
flag[j]=0;
}
printf("%d\n",res);
res=0;
for(int i=2;i<n;i++)
if(ans[i])
{
ans[i]=0;
printf("%d ",i);
}
printf("\n");
}
return 0;
}
/*
2
4 3
1 2
2 3
3 4
5 5
1 2
2 3
3 4
4 5
4 1
*/
update on 17:22 1/24
40 pts
#include<bits/stdc++.h>
using namespace std;
const int N=400100;
int T,n,m;
int head[11][N],nxt[11][N],to[11][N];
int low[11][N],dfn[11][N],flag[11][N];
int t=1,root,ans,mark[11][N],f[11][N],fa[11][N];
queue<int>q;
void add(int u,int v)
{
nxt[T][++t]=head[T][u];
head[T][u]=t;
to[T][t]=v;
}
void tarjan(int x)
{
if(x==n)
mark[T][x]=1;
low[T][x]=dfn[T][x]=++t;
int fl=0;
for(int i=head[T][x];i;i=nxt[T][i])
{
int y=to[T][i];
if(!dfn[T][y])
{
tarjan(y);
low[T][x]=min(low[T][x],low[T][y]);
if(low[T][y]>=dfn[T][x])
{
fl++;
if(x!=root||fl>1)
flag[T][x]=true;
}
}
else
low[T][x]=min(low[T][x],dfn[T][y]);
}
return ;
}
inline void bfs(int x)
{
queue<int>que;
que.push(x);
f[T][x]=1;
while(!que.empty())
{
x=que.front();
que.pop();
for(int i=head[T][x],y;i;i=nxt[T][i])
{
y=to[T][i];
if(f[T][y])
continue;
if(y==n)
{
while(x!=1)
{
mark[T][x]=1;
x=fa[T][x];
}
return ;
}
que.push(y);
f[T][y]=1;
fa[T][y]=x;
}
}
return ;
}
int main()
{
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
cin>>T;
while(T--)
{
t=root=ans=0;
cin>>n>>m;
for(int i=1,u,v;i<=m;i++)
{
scanf("%d%d",&u,&v);
if(u==v)
continue;
add(u,v);
add(v,u);
}
t=0;
for(int i=1;i<=n;i++)
if(!dfn[T][i])
{
root=i;
tarjan(i);
}
bfs(1);
for(int i=2;i<n;i++)
if(flag[T][i]&&mark[T][i])
{
ans++;
q.push(i);
}
cout<<ans<<'\n';
while(!q.empty())
{
printf("%d ",q.front());
q.pop();
}
printf("\n");
}
return 0;
}
update on 6.26
100pts
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int T,n,m,ans,f;
int head[N],nxt[N],to[N],tot;
int dfn[N],low[N],cnt,flag[N];
stack<int>s;
vector<int>v[N];
priority_queue<int>q;
inline void init()
{
// memset(head,0,sizeof(head));
// memset(nxt,0,sizeof(nxt));
// memset(to,0,sizeof(to));
// memset(dfn,0,sizeof(dfn));
// memset(low,0,sizeof(low));
// memset(flag,0,sizeof(flag));
for(int i=1;i<=n*2;i++)
head[i]=dfn[i]=low[i]=flag[i]=0;
for(int i=1;i<=tot;i++)
nxt[i]=to[i]=0;
for(int i=1;i<=cnt;i++)
v[i].clear();
ans=tot=f=0;
return ;
}
inline void add(int u,int v)
{
nxt[++tot]=head[u];
head[u]=tot;
to[tot]=v;
return ;
}
inline void tarjan(int x)
{
dfn[x]=low[x]=++tot;
s.push(x);
for(int i=head[x],y,z;i;i=nxt[i])
{
y=to[i];
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]==dfn[x])
{
++cnt;
do
{
z=s.top();
s.pop();
v[cnt].push_back(z);
v[z].push_back(cnt);
}while(z!=y);
v[x].push_back(cnt);
v[cnt].push_back(x);
}
}
else
low[x]=min(low[x],dfn[y]);
}
return ;
}
inline void dfs(int x)
{
if(x==n)
return ;
for(auto it : v[x])
if(!flag[it])
{
flag[it]=x;
dfs(it);
}
return ;
}
int main()
{
freopen("home.in","r",stdin);
freopen("home.out","w",stdout);
cin>>T;
// T=1;
while(T--)
{
init();
scanf("%d%d",&n,&m);
cnt=n;
for(int i=1,u,v;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
tot=0;
for(int i=1;i<=n;i++)
if(!dfn[i])
{
tarjan(i);
s.pop();
}
dfs(1);
int x=flag[n];
while(x!=1)
{
if(x<n)
{
ans++;
q.push(-x);
}
x=flag[x];
}
printf("%d\n",ans);
while(!q.empty())
{
printf("%d ",-q.top());
q.pop();
}
printf("\n");
}
return 0;
}
T3
题意:两种颜色的寿司围成一个圈,每次移动相邻的两个,问最少多少次能让他们分开(形成两个连续的区域)
刚开始只有乱搞的思路,题目不知道为啥还疯狂暗示你这道题用数据结构(平衡树)
后来想到可以转换成链然后 前缀和 + 差分
想来会有很多问题,但没啥时间所以没想更优的做法
赛事+赛后代码 15pts(不会)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
int main()
{
cin>>T;
while(T--)
{
scanf("%s",c);
len=strlen(c);
for(int i=len;i>=1;i--)
c[i]=c[i-1];
c[0]=0;
for(int i=1;i<=len;i++)
if(c[i]=='R')
b[++tb]=i;
for(int i=tb+1;i<=tb*2;i++)
b[i]=b[i-tb]+len;
for(int i=1;i<=tb*2;i++)
q[i]=q[i-1]+b[i]-b[i-1]-1;
ans=N<<5;
for(int i=1;i<=tb*2;i++)
{
int res=0;
for(int j=1;j<=tb;j++)
{
if(j==i||j+tb==i)
continue;
res+=min(abs(q[j]-q[i]),abs(q[j+tb]-q[i]));
}
ans=min(ans,res);
}
printf("%d\n",ans);
}
return 0;
}
/*
1
BBRBBRBBBRRR
*/
想象中的:
实际上的:
对了,走的时候被 \(Huge\) D了,让我期末考好一点,就当给期末攒 \(RP\) 了吧
顺便放上本次放假最大收获:
(可惜不知道为啥游戏里新皮肤两只眼睛跟长分家了一样,过活动的时候老难受了)