Codeforces Round #425 (Div. 2)
补完题才发现自己真的失了智,好久没打CF,竟然又是死在小细节上。错过多少次涨分良机。
水题,求n/k的奇偶性。注意数据范围
int main()
{
ll n,k;
while(cin>>n>>k)
{
ll ans=n/k;
if(ans&1) puts("YES");
else puts("NO");
}
return 0;
}
水题,暴力匹配,细节处理。下标未判断好结果跪在终判。。
题意:给你一些good字母,然后给你一个模式串P(由小写字母、?、* 组成)。模式串中的?可以用任意good字母代替,而*可以为任意长度的bad字符组成。Q次查询,每次输入一个字符串S,判断S是否能和模式串P匹配。
思路:找到*的位置,若没有*则必然找到len(P),假设从0开始。暴力匹配*前面的与*后面的是否匹配。然后S中如果还有剩余未匹配的,则判断这一段未匹配的是否出现过good字母。
int vis[27];
char s[N],p[N];
int main()
{
memset(vis,0,sizeof(vis));
scanf("%s",s);
for(int i=0; s[i]!='\0'; i++) vis[s[i]-'a']++;
scanf("%s",p);
int len=strlen(p),pos=0,tmp=0;
while(p[pos]!='*'&&pos<len) pos++;// 找到*的位置
tmp=len;
if(pos<len) tmp--;
int Q;
scanf("%d",&Q);//暴力匹配*前面的和*后面的,然后看中间的是否符合
while(Q--)
{
scanf("%s",s);
int f=1,num=0,num1=0,leng=strlen(s);
if(tmp>leng||(tmp==len&&len<leng)) puts("NO");
else
{
for(int i=0; i<pos&&f; i++)
{
if(s[i]==p[i]||(p[i]=='?'&&vis[s[i]-'a'])) num++;
else f=0;
}
for(int i=len-1,j=leng-1;i>pos&&j>=0&&f; i--,j--)
{
if(s[j]==p[i]||(p[i]=='?'&&vis[s[j]-'a'])) num1++;
else f=0;
}
num1=leng-num1-1;
for(int i=num;i<=num1&&f;i++)//这里下标范围需要注意一下
if(vis[s[i]-'a']) f=0;
if(f) puts("YES");
else puts("NO");
}
}
return 0;
}
D. Misha, Grisha and Underground
LCA,贪心。补题的时候不知道怎么就突然会了,但比赛的时候思路却总是模糊,一直跪在第4组。
题意:给你一颗生成树,保证n-1条边将所有的点都连起来了。Q次查询,每次给你三个数a,b,c。选一个点作为出发点,选一个点作为终点,这条最短路径上的点都标记起来,求剩下的一个点到终点的最短路中最多能碰到多少个标记。
思路:题目提示的很清楚了只有n-1条边,我们可以以1号节点作为根,建一颗树。任意两个点只有一条路径,也是最短路径。但查询次数为1e5。所以必然要求在log(n)的复杂度内找到答案,这也再一步证实了LCA的可行性。确定了算法,那怎么选取起点和终点呢,这就是贪心了。我们可以简单画几个样例图,三个点不定的情况下要从一个未选中的点到另外两个点的路径中所覆盖的点最多,很明显要从深度(dep)大的开始走。怎么证明呢?假设我们选取a,b作为起点终点,c作为中间点,求c->b与a->b所重复经过的点最多。来分析看看,a->b必然经过LCA(a,b),c->b必然经过LCA(c,b)。但这两个lca不一定在同一条路径上,我们还需要知道LCA(a,c)。现在知道了任意两个点的lca,从贪心的策略看,必然要从深度最大的开始走,这样经过的重复点才可能尽量多,想想看是不是这样。所以可以枚举所有情况,注意判断确定最优解。但我们都已经知道了所有的lca,只需枚举终点,然后都从度最大的lca作为起点,到终点的距离便是答案了,选一个最优解即可。
可能解释得有点不明白,确实在贪心那里有点模糊,导致比赛中一直不知道怎么贪心。
int n,q,p[N][18],d[N];
int tot,head[100005];
struct node
{
int to,next;
} e[2*N];
void add(int u,int v)
{
e[tot].to=v;
e[tot].next=head[u];
head[u]=tot++;
}
void dfs(int u,int fa,int dep)
{
d[u]=dep;
p[u][0]=fa;
for(int i=head[u]; i+1; i=e[i].next)
{
int to=e[i].to;
if(to!=fa) dfs(to,u,dep+1);
}
}
void init()
{
dfs(1,0,1);
for(int k=0; k<17; k++)
for(int v=1; v<=n; v++)
{
if(p[v][k]<0) p[v][k+1]=-1;
else p[v][k+1]=p[p[v][k]][k];
}
}
int lca(int u,int v)
{
if(d[u]>d[v]) swap(u,v);
for(int k=0; k<18; k++)
if((d[v]-d[u])>>k&1)
v=p[v][k];
if(u==v) return u;
for(int k=17; k>=0; k--)
if(p[u][k]!=p[v][k])
{
u=p[u][k];
v=p[v][k];
}
return p[u][0];
}
int dis(int u,int v)
{
int pos=lca(u,v);
return d[u]+d[v]-2*d[pos]+1;
}
int solve(int a,int b,int c)
{
int ans=0;
int fa1=lca(a,c),fa2=lca(b,c),fa3=lca(a,b);
int fa=d[fa1]>=d[fa2]?fa1:fa2;
fa=d[fa3]>=d[fa]?fa3:fa;//选择深度最大的作为起点
ans=max(ans,dis(fa,a));
ans=max(ans,dis(fa,b));
ans=max(ans,dis(fa,c));
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&q))
{
int a,b,c;
memset(head,-1,sizeof(head));
for(int i=2; i<=n; i++)
{
scanf("%d",&a);
add(a,i);
add(i,a);
}
init();
while(q--)
{
scanf("%d%d%d",&a,&b,&c);
printf("%d\n",solve(a,b,c));
}
}
return 0;
}
只有去做了才会发现自己的不足,会做和能AC不等价,CF是一个很好的训练场,在不影响室友的前提下尽量做吧。
这场犯的错误:B题虽然一遍过初判,但下标应该是num开始,而不是num+1开始,长度为num实际对应0-num-1。导致终判跪在26组。字符串这里有点模糊了,石乐志。D题很快想到是LCA,却一直不知道怎么贪心,后一个半小时就一直稀里糊涂的再乱猜,没有仔细去分析,白白浪费这么多时间。细节处理上有待提高,还有切莫急功近利,现在比以前要好多了,放宽心,用最佳的状态去做,结果不用太在意这样结果反而会出奇迹。从近期比赛情况看可以把自己的定位适当提升一点点了,CF不仅仅满足在涨分这个乐趣了,合理分析自己的排名,逐渐提高。