noip 模拟 7


我花了我多久的rp啊……
考试经过
这次是三道题,依旧先看一遍,然后从头开始做
T1一看,这好像是KMP?等等,我好像忘了啊你个废,没事哈希也能做,On似乎可以呀,一波操作,我是不是要A题了?
转到T2,图论题,应该和tarjan有关,好像是割点,但板又双叒叕忘了不长记性,看见数据范围,先拿10分树分,前6个点可以暴力,40不少,过
T3,乍一看不太可做,一开始想到图论后来发现不对,然后想到一个n2的做法,觉得正确性有了直接开打,觉得最好是40分,但感觉常数有点大,估了25
剩下时间主要在检查,发现了包括return 0写进循环里面的sb错误,优化了几个常数虽然基本是负优化
考完了从后往前找自己,结果:100+30+20=150,就水成rank1了……
但真的很菜啊,教练说上届学长们都200多,自闭了……
太弱了,还是太弱了
T1 匹配
签到题,哈希模板直接带走,罗列几个常见错误:
1.模数取小的
2.数组没开够的
3.while写成if的
4.忘了清空的
5.板子没记住的
6.快读写挂的
觉得能过主要是忘了KMP,要是记得的话可能我也写假了,淦
T2 回家
题面就是让你求无向图两点之间的必经点,暴力有30,正解是tarjan求割点
注意答案一定是割点,但割点不一定是答案,因为一个割点去掉后图分成两个连通块,可能1和n仍然在一个连通块里,所以直接输出割点不对
法一
先求割点,再用点双缩点,建新图,建出来的肯定是树,以包含1节点的点双(任选一个)为根,dfs一遍,从包含n节点的点双(任选一个)回溯,我用的是跳父亲,回到根节点,途中经过的割点就是答案,答案排遍序输出就行
#include <bits/stdc++.h>
using namespace std;
int n,m;
struct node{
int from,to,next;
}a[900005];
int head[200050],mm=1;
inline void add(int x,int y)
{
a[mm].from=x;a[mm].to=y;
a[mm].next=head[x];head[x]=mm++;
}
bool v[400005];int num[200050],nu=0;
inline void get(int x){num[++nu]=x;}
int dfn[200050],low[200050],p;
bool ge[200050];stack <int> s;
vector <int>dcc[200050];int dnum,gen1,genn;
void tarjan(int x)
{
dfn[x]=low[x]=++p;s.push(x);
int ch=0;
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].to;
if(!dfn[y])
{
tarjan(y);low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])
{
ch++;
if(x!=1||ch>1)ge[x]=1;
dnum++;int temp;
do{
temp=s.top();
s.pop();
if(temp==1)gen1=dnum;
if(temp==n)genn=dnum;
dcc[dnum].push_back(temp);
}while(temp!=y);
dcc[dnum].push_back(x);
if(x==1)gen1=dnum;
if(x==n)genn=dnum;
}
}
else low[x]=min(low[x],dfn[y]);
}
}
struct newnode{
int from,to,next;
}b[1000005];
int nhead[500005],nmm=1;
inline void nadd(int x,int y)
{
b[nmm].from=x;b[nmm].to=y;
b[nmm].next=nhead[x];nhead[x]=nmm++;
}
int mp[400005],sb[200500];
inline void getnu()
{
int ga=dnum;
for(int i=1;i<=n;i++)
if(ge[i])sb[i]=++ga,mp[sb[i]]=i;
for(int i=1;i<=dnum;i++)
for(int j=0;j<dcc[i].size();j++)
{
int y=dcc[i][j];
if(ge[y])nadd(i,sb[y]),nadd(sb[y],i);
}
}
int fa[200500];
inline void dfs(int x)
{
v[x]=1;
for(int i=nhead[x];i;i=b[i].next)
{
int y=b[i].to;
if(v[y])continue;
fa[y]=x;dfs(y);
}
}
inline void clear()
{
memset(a,0,sizeof(a));mm=1;
memset(head,0,sizeof(head));
memset(v,0,sizeof(v));
memset(num,0,sizeof(num));nu=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(0));p=0;
memset(ge,0,sizeof(ge));
while(!s.empty())s.pop();
for(int i=1;i<=n;i++)dcc[i].clear();
gen1=genn=0;dnum=0;
memset(b,0,sizeof(b));nmm=1;
memset(nhead,0,sizeof(nhead));
memset(mp,0,sizeof(mp));
memset(fa,0,sizeof(fa));
}
signed main()
{
int T;cin>>T;
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
if(x==y)continue;
add(x,y);add(y,x);
}
tarjan(1);getnu();dfs(gen1);
int p=fa[genn];
while(p&&p!=gen1)
{
if(p>dnum&&mp[p]!=1&&mp[p]!=n)get(mp[p]);
p=fa[p];
}
sort(num+1,num+nu+1);
printf("%d\n",nu);
for(int i=1;i<=nu;i++)printf("%d ",num[i]);
printf("\n");clear();
}
return 0;
}
由于我太菜了,所以常数挺大的,主要是memset。还有是数组要开够
法二
在求出割点后不缩点,直接在过程中看能不能回溯过去,如果不能证明这个点不是答案,如果行就加进集合,%XIN队
T3 寿司
我n2的暴力本来有四十分,结果由于memset了一大堆200万的数组,被卡到20,你也是,既然冲着部分分去的,数组开那么大干啥啊?
感觉方法挺多,见此题专门博客
考试反思
1.板子一定要记牢,多复习,别以为以后就会了,现在不行根本没有以后
2.大数组慎用memset,手动清空或设法覆盖
3.深入思考,不要轻易放弃
4.看看上一届学长,不能懈怠,更不能颓

浙公网安备 33010602011771号