背景

时间分配与得分成反比,T1 20min 73pts,T2 1h 30pts,T3 2h 15pts(没有更新tot值,本来应该是40pts的,算是本次考试中最遗憾的地方了吧),改起来就是T3比较难改,其他的还好。。。

两位队爷没考,战神也出了点意外,让我们这些菜鸡钻了空子。

多组数据一定要清零

T1 匹配

前言

我就没想到模拟赛会出这种水题,正解的话hash与KMP都可以,只可惜我只留下20分钟给这题,实力有限,时间有限,就草草打了个暴力。出乎意料整到了\(73pts\)属实出乎意料。。

解题思路

就讲一下Hash的做法吧,至于KMP做法请参考@fengwu的blog

对于A的前缀可以用常规方法直接依次算,使幂次方数随着下标的递增而递减,

但是为了迎合后面对于B后缀的计算,我们这里令幂次方随下标递增而递增,先初始化一下,整出base值的若干次方存入p数组,因此求出A的Hash:

\[ha[i]=ha[i-1]+s[i] \times p[i-1]
\]

然后对于B的后缀就可以直接从后往前正常扫就好了。

\[ha[i]=ha[i+1]\times base+s[i]
\]

之后我们就可以直接A的前缀和B的后缀两个串直接判等了,数组要开的大一点。

code

#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
const int N=2e5+10,base=13331;
int T,n,m,ans,p[N],h1[N],h2[N];
char ch,s[N],s2[N];
bool vis[30];
#undef int
int main()
{
#define int unsigned long long
scanf("%llu",&T);
p[0]=1;
for(int i=1;i<N;i++)
p[i]=p[i-1]*base;
while(T--)
{
memset(vis,false,sizeof(vis));
ans=0;
scanf("%llu%llu",&n,&m);
scanf("%s",s+1);
getchar();
scanf("%c",&ch);
for(int i=1;i<=n;i++)
vis[s[i]-'a']=true;
if(!vis[ch-'a'])
{
cout<<0<<endl;
continue;
}
for(int i=1;i<=m;i++)
s2[i]=s[i];
s2[++m]=ch;
for(int i=1;i<=m;i++)
{
h1[i]=h1[i-1]+s[i]*p[i-1];
// cout<<h1[i]<<" "<<h1[i-1]<<" "<<s[i]<<" " <<p[i-1]<<endl;
}
// cout<<endl;
h2[m+1]=0;
for(int i=m;i>=1;i--)
h2[i]=h2[i+1]*base+s2[i];
/* for(int i=1;i<=m;i++)
cout<<h2[i]<<' ';*/
for(int len=m;len>=1;len--)
{
int ha1=h1[len],ha2=h2[m-len+1];
if(ha1==ha2)
{
ans=len;
break;
}
}
printf("%llu\n",ans);
}
return 0;
}

T2 回家

前言

考场上这个题第一眼就看出了是Tarjan割点,然后想了想Tarjan超出了我的能力范围,就老老实实打暴力骗分去了。。。

解题思路

解题思路来自@fengwu,有一种双端扫的感觉。。。

dfn[i]储存i的时间戳,low[i]表示i所在联通块的最早dfs到的点的时间戳(话说这东西Tarjan不是讲过吗)vis用于从n节点向上进行更新。(vis[n]要初始化为true)

然后从1节点开始进行dfs,所连通的节点的状态有两种情况:

  1. 该节点未被扫过: 先对于该节点进行dfs,然后更新现在节点的low值,如果vis[to]是true也就是联通节点可以走到n,向上更新现在节点的vis为true。如果现在节点的时间戳小于等于联通节点所在联通块的最小时间戳并且联通节点可以到达n节点,那么这个点就是一个必经点。

  2. 该节点被扫过:用联通节点的时间戳来更新现在节点的low,为什么不用联通节点的low呢,以下图为例:

    假设现在节点是6号节点,要扫3号节点了,3号节点的时间戳是7,low是1如果我们用low来更新,那无异与除7之外ia所有节点都是一个联通块的,这显然是不可以的。

最后输出就好了,注意必经点要排一下序。

code

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int T,n,m,cnt,tim,dian[N],dfn[N],low[N];
int tot,ver[N<<1],head[N],nxt[N<<1];
bool vis[N],b[N];
inline void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void init()
{
tot=cnt=tim=0;
memset(vis,false,sizeof(vis));
memset(head,0,sizeof(head));
memset(nxt,0,sizeof(nxt));
memset(ver,0,sizeof(ver));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
}
void dfs(int x)
{
dfn[x]=low[x]=++tim;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(!dfn[to])
{
dfs(to);
low[x]=min(low[to],low[x]);
if(vis[to])
vis[x]=true;
if(low[to]>=dfn[x])
if(x!=1&&vis[to])
dian[++cnt]=x;
}
else
low[x]=min(low[x],dfn[to]);
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
init();
scanf("%d%d",&n,&m);
for(int i=m,x,y;i>=1;i--)
{
scanf("%d%d",&x,&y);
if(x==y)
{
m--;
continue;
}
add_edge(x,y);
add_edge(y,x);
}
vis[n]=true;
dfs(1);
sort(dian+1,dian+cnt+1);
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)
printf("%d ",dian[i]);
printf("\n");
}
return 0;
}

T3 寿司

前言

挺可惜的,考场上2h想到了40分的打法,也打出来了,就是tot没有清零喜提15pts。

解题思路

暴力

暴力的话40pts比较好想,先化环为链,再对于每一个长度为n的区间,先求一下每个R节点之前以及之后的R分别到两端的距离和,用前缀后缀和维护,然后对于移到左边的个数进行枚举,更新。设到左侧的个数为cnt,因为不是每一个点都要移到端点,不难发现我们需要减去一部分:

\[\sum\limits_{i=1}^{cnt-1} i=\dfrac{cnt\times(cnt-1)}{2}
\]

对于右端点的处理也是如此,优化的话就是二分一下对于区间的右半部分向右移,左半部分向左移,不难发现中间节点的坐标与区间是单调的,我们可以暴力处理第一个,对于后面的挨个搜就行了,还需要一个全局前缀和复杂度为\(O(n)\)但是我们打出来。。。可以参考 @zxb的代码,下面给出暴力的代码

\[一定要清零tot值
\]

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int T,n,ans,tot,temp,cnt,ch[N],q[N],h[N];
char s[N<<1];
void work(int x)
{
// memset(q,0,sizeof(q));
// memset(h,0,sizeof(h));
int r=x-1,lb=0;
for(int i=x;i<=2*n;i++)
{
r++;
if(s[i]=='B')
lb++;
if(lb==tot)
break;
}
if(lb!=tot)
return ;
for(int i=x;i<=r;i++)
if(s[i]=='R')
{
ch[++cnt]=i;
q[cnt]=q[cnt-1]+i-x;
}
h[cnt+1]=0;
for(int i=cnt;i>=0;i--)
h[i]=h[i+1]+r-ch[i];
for(int i=0;i<=cnt;i++)
{
int sum=0;
sum=q[i]-(i+1-1)*(i-1)/2+h[i+1]-(cnt-i+1-1)*(cnt-i-1)/2;
if(sum>=0)
ans=min(ans,sum);
}
}
#undef int
int main()
{
#define int register long long
#define ll long long
scanf("%lld",&T);
while(T--)
{
ans=INT_MAX;
tot=0;
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++)
tot+=(s[i]=='B');
if(n-tot>tot)
{
for(int i=1;i<=n;i++)
s[i]=(s[i]=='B')?'R':'B';
tot=n-tot;
}
for(int i=1;i<=n;i++)
s[i+n]=s[i];
for(int i=1;i<=n;i++)
{
if(s[i]=='R')
continue;
temp=cnt=0;
work(i);
}
printf("%ld\n",ans);
}
return 0;
}

正解

思路非常的巧妙来自zhanshen@zero4338,l[i]表示i到左端点的距离(也就是该点左边B的个数)可以得出以下式子:

\[\sum\limits_{i=1}^{tot_R}\min(l[i],r[i])
\]
\[=\sum\limits_{i=1}^{tot_R}\dfrac{l[i]+r[i]-|l[i]-r[i]|}{2}
\]
\[=\dfrac{tot_B\times tot_R}{2}+\sum\limits_{i=1}^{tot_R}\dfrac{|l[i]-r[i]|}{2}
\]

接下来我们只需要处理后半段就行了,对于后半段,我们先压入小根堆里,然后再对于B和R的情况分别进行处理:

  1. 扫到B字符:首先把B移到右边端点后所有的l都减了1,r都加了1,因此加上负数数量*2,我们需要将之前处理P字符的进行更新,如果堆顶的值大于扫过的B数量*2,直接break,剩下的交给后面处理,对于相等的,计算移动的R字符本身,给sum减去2,处理完堆里的之后再将正数的贡献加上就好了。
  2. 扫到R字符:先不做处理,将它的贡献压入堆里,然后更新正负数的值。

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int T,n,sum,maxn,tb,tr,z,f,tot,l[N],r[N];
char s[N];
priority_queue<int,vector<int>,greater<int> > q;
void init()
{
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
z=f=tb=tr=sum=tot=maxn=0;
while(!q.empty())
q.pop();
}
#undef int
int main()
{
#define int register long long
#define ll long long
scanf("%lld",&T);
while(T--)
{
init();
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++)
{
l[i]=l[i-1];
if(s[i]=='B')
{
tb++;
l[i]++;
}
else
tr++;
}
for(int i=1;i<=n;i++)
if(s[i]=='R')
{
r[i]=tb-l[i];
sum+=abs(l[i]-r[i]);
if(l[i]-r[i]>0)
{
q.push(l[i]-r[i]);
z++;
}
else f++;
}
maxn=max(maxn,sum);
for(int i=1;i<n;i++)
{
if(s[i]=='B')
{
sum+=2*f;
tot++;
while(!q.empty())
{
if(q.top()>2*tot)
break;
if(q.top()==2*tot)
sum-=2;
z--;
f++;
q.pop();
}
sum-=2*z;
maxn=max(maxn,sum);
}
else
{
z++;
f--;
q.push(tb+2*tot);
}
}
printf("%lld\n",(tr*tb-maxn)/2);
}
return 0;
}

6.11考试总结(NOIP模拟7)的更多相关文章

  1. 2021.8.11考试总结[NOIP模拟36]

    T1 Dove玩扑克 考场并查集加树状数组加桶期望$65pts$实际$80pts$,考后多开个数组记哪些数出现过,只扫出现过的数就切了.用$set$维护可以把被删没的数去掉,更快. $code:$ 1 ...

  2. 2021.10.11考试总结[NOIP模拟74]

    T1 自然数 发现\(mex\)是单调不降的,很自然地想到用线段树维护区间端点的贡献. 枚举左端点,用线段树维护每个右端点形成区间的\(mex\)值.每次左端点右移相当于删去一个数. 记\(a_i\) ...

  3. 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]

    6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...

  4. 5.23考试总结(NOIP模拟2)

    5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...

  5. 5.22考试总结(NOIP模拟1)

    5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...

  6. 「考试」noip模拟9,11,13

    9.1 辣鸡 可以把答案分成 每个矩形内部连线 和 矩形之间的连线 两部分 前半部分即为\(2(w-1)(h-1)\),后半部分可以模拟求(就是讨论四种相邻的情况) 如果\(n^2\)选择暴力模拟是有 ...

  7. 2021.9.17考试总结[NOIP模拟55]

    有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...

  8. [考试总结]noip模拟23

    因为考试过多,所以学校的博客就暂时咕掉了,放到家里来写 不过话说,vscode的markdown编辑器还是真的很好用 先把 \(noip\) 模拟 \(23\) 的总结写了吧.. 俗话说:" ...

  9. [考试总结]noip模拟11

    菜 这次考试又是骗了一堆分数... 然而其实一个正解都没写... \(T1\) 的方法说实话确实不是很正统.... 然而却 \(A\) 了... 在打完 \(T1\) 后拍了老长时间... 然后就耽搁 ...

随机推荐

  1. php 获取某文件夹(比如共享文件夹)下图片并下载并压缩成zip

    1.前端部分:直接请求 2.后端php //zip下载public function downZip(){ $pro_code = "test"; //zip名称 //获取列表 $ ...

  2. windows下使用dos命令手工与ntp服务器同步系统时间

    管理员模式的命令窗口 net stop w32time &w32tm /unregister &w32tm /register &net start w32time & ...

  3. CAS的理解

    CAS(CompareAndSweep)工作方式 ​ CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被 ...

  4. Linux下script命令录制、回放和共享终端操作script -t 2> timing.log -a output.session # 开始录制

    Linux下script命令录制.回放和共享终端操作 [日期:2018-09-04] 来源:cnblogs.com/f-ck-need-u  作者:骏马金龙 [字体:大 中 小]   另一篇终端会话共 ...

  5. LTP--linux稳定性测试 linux性能测试 ltp压力测试 ---IBM 的 linux test project

    LTP--linux稳定性测试 linux性能测试 ltp压力测试 ---IBM 的 linux test project Peter盼 2014-04-23 11:25:49  20302  收藏  ...

  6. Linux性能监控与分析之--- CPU

    Linux性能监控与分析之--- CPU 望月成三人关注 2016.07.25 18:16:12字数 1,576阅读 2,837 CPU性能指标 用户进程使用CPU的比率 系统进程使用CPU的比率 W ...

  7. Yarn 集群环境 HA 搭建

    环境准备 确保主机搭建 HDFS HA 运行环境 步骤一:修改 mapred-site.xml 配置文件 [root@node-01 ~]# cd /root/apps/hadoop-3.2.1/et ...

  8. shell初学之PHP

    初次接触脚本,写了一个通过Apache实现PHP动态网站的脚本: #!/bin/bash yum -y install php rm -rf /etc/httpd/conf.d/welcome.con ...

  9. 惊奇发现KEIL也可以C++编译了

    在Github上面浏览下载的一个工程,可以用KEIL打开,但是我竟然找不到mian函数.找了一圈发现是用到了面向对象的编程方法,那就必须支持C++,他怎么实现的呢? 看配置工程:明显的一个不一样 -- ...

  10. 『动善时』JMeter基础 — 28、JMeter函数助手详解

    目录 1.函数助手介绍 2.函数助手中的函数分类 3.常用函数介绍 (1)__Random函数 (2)__counter函数 (3)__time函数 (4)__property函数 (5)__setP ...