Codeforces Round #316 (Div. 2) D、E

Problem D:

题意:给定一棵n个点树,每个点有一个字母,有m个询问,每次询问某个节点x的子树中所有深度为k的点能否组成一个回文串

分析:一堆点能组成回文串当且仅当数量为奇数的字母不多于1个,显然这个状态可以用二进制表示

        那么对于单个询问如何快速找到所有符合要求的点呢?

        这里可以考虑树的dfs序,我们把深度相同的点按照dfs序加入集合中,易知在同一颗子树中的点肯定形成了一个连续的区间。

        因此每次可以通过二分子树根节点的进入dfs序的时间和出dfs序的时间来找到这个区间 

        找到区间后可以根据预处理出的异或前缀和直接得到这个区间的二进制状态,再check一下即可

        复杂度 n+m*(logn+26)

代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 vector<int>g[500010];
 4 int a[500010];
 5 char s[500010];
 6 int in[500010];
 7 int out[500010];
 8 int n,m;
 9 int t=0;
10 struct node
11 {
12     int t,val;
13     bool operator <(const node &a)const
14     {
15         return t<a.t;
16     }
17 };
18 vector<node>ans[500010];
19 void dfs(int now,int d)
20 {
21     in[now]=++t;
22     ans[d].push_back(node{t,ans[d][ans[d].size()-1].val^a[now]});
23     for(int i=0;i<(int)g[now].size();i++)
24     {
25         int to=g[now][i];
26         dfs(to,d+1);
27     }
28     out[now]=++t;
29 }
30 int main()
31 {
32     scanf("%d%d",&n,&m);
33 
34 
35     for(int i=2;i<=n;i++)
36     {
37         int x;
38         scanf("%d",&x);
39         g[x].push_back(i);
40     }
41 
42     scanf("%s",s);
43     for(int i=1;i<=n;i++)
44     {
45         a[i]=1<<(s[i-1]-'a');
46         ans[i].push_back(node{0,0});
47     }
48     dfs(1,1);
49     while(m--)
50     {
51         int p,x;
52         scanf("%d%d",&p,&x);
53         auto l=lower_bound(ans[x].begin(),ans[x].end(),node{in[p],0});
54         auto r=upper_bound(ans[x].begin(),ans[x].end(),node{out[p],0});
55         l--;
56         r--;
57         int st=((*l).val)^((*r).val);
58         int cnt=0;
59         for(int i=0;i<26;i++)
60         {
61             if(st&(1<<i))
62                 cnt++;
63         }
64         if(cnt>1)
65         {
66             puts("No");
67         }
68         else
69         {
70             puts("Yes");
71         }
72     }
73     return 0;
74 }
View Code

Problem E:

题意:给一个n*m的棋盘,每个格子有一个字母,现在从1,1走到n,m,每次只能向右或者向下走,问走的路径上形成的字符串是回文串的方案书是多少

分析:从回文串的中点,即x+y=(n+m)/2的地方开始dp ,dp[i][j][k]存储当前回文长度为2*i ,左端点的x为i,右端点的x为j的回文串有多少个。

        然后按照对角线递推即可。转移时注意必须满足左端点的x必须小于右端点,y必须大于右端点

代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int n,m;
 4 int dp[2][505][505];
 5 char s[505][505];
 6 const int mod=1e9+7;
 7 int main()
 8 {
 9     scanf("%d%d",&n,&m);
10     for(int i=0; i<n; i++)
11     {
12         scanf("%s",s[i]);
13     }
14     int ok=1;
15     for(int i=(n+m)/2-1; i>=0; i--)
16     {
17         memset(dp[i%2],0,sizeof(dp[i%2]));
18         int j=n+m-2-i;
19         for(int x1=0; x1<n; x1++)
20         {
21             int y1=i-x1;
22             if(y1<0||y1>=m)
23                 continue;
24             for(int x2=0; x2<n; x2++)
25             {
26 
27                 int y2=j-x2;
28                 if(y2<0||y2>=m)
29                     continue;
30                 if(x1>x2||y1>y2)
31                     continue;
32                 if(s[x1][y1]==s[x2][y2])
33                 {
34                     if(ok==1)
35                     {
36                         dp[i%2][x1][x2]=1;
37                         continue;
38                     }
39                     for(int a=0; a<2; a++)
40                     {
41                         if(x1+a>=n)
42                             continue;
43                         for(int b=-1; b<1; b++)
44                         {
45                             if(x2+b<0)
46                                 continue;
47                             dp[i%2][x1][x2]=(dp[i%2][x1][x2]+dp[(i+1)%2][x1+a][x2+b])%mod;;
48                         }
49                     }
50                 }
51             }
52         }
53         ok++;
54     }
55     printf("%d\n",dp[0][0][n-1]);
56     return 0;
57 }
View Code

 

posted @ 2015-08-15 15:04  PlasticSpirit  阅读(169)  评论(0编辑  收藏  举报