2018.09.23模拟总结(T2)

T1,T3我就不说啦,反正也不会。主要想讲的是T2.

  T2用了一个神奇的算法:折半搜索。

  因为这个坑爹的数据范围告诉我们暴搜或是状压会TLE,而一半刚好能卡过去。

  折半搜索其实跟暴搜没什么区别,就是折了半(废话)。拿这道题为例,暴搜就是在长度为2n的序列中找出所有长度为n的序列不妨设为s1, 那么剩下的就是s2,然后判断s1和翻转后的s2是否相等,复杂度O(C(n, 2n))。

  折半搜索就是在前一半的序列中找出所有长度为n / 2的序列,也就是s1的前一半s1',剩下的作为s2的后一半(因为要反转)s2'。然后把这种状态,即s1' + s2'记下来,用map实现最方便。接下来我们在[n + 1, 2n]这个区间里暴搜,找到s1的后一半s1'',s2的前一半s2''(当然还要反转),为了验证两个合一块的字符串是否相等,我们只要证明s2'' + s1''是否存在过,如果存在,就说明找到了一组,ans++。时间复杂度O(C(n / 2, n) * 2)。理论上也是暴搜,却快了不少。

  代码实现的时候用string最方便不过,不仅用加号就能代替strcpy,而且还自带反转函数。

代码就是两遍状压暴搜

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 #include<map>
12 #include<string>
13 using namespace std;
14 #define enter puts("") 
15 #define space putchar(' ')
16 #define Mem(a, x) memset(a, x, sizeof(a))
17 #define rg register
18 typedef long long ll;
19 typedef double db;
20 const int INF = 0x3f3f3f3f;
21 const db eps = 1e-8;
22 const int maxn = 40;
23 inline ll read()
24 {
25     ll ans = 0;
26     char ch = getchar(), last = ' ';
27     while(!isdigit(ch)) {last = ch; ch = getchar();}
28     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
29     if(last == '-') ans = -ans;
30     return ans;
31 }
32 inline void write(ll x)
33 {
34     if(x < 0) x = -x, putchar('-');
35     if(x >= 10) write(x / 10);
36     putchar(x % 10 + '0');
37 }
38 void MYFILE()
39 {
40 #ifndef mrclr
41     freopen("string.in", "r", stdin);
42     freopen("string.out", "w", stdout);
43 #endif
44 }
45 
46 int n;
47 char s[maxn];
48 string s1, s2;
49 map<string, int> mp;
50 ll ans = 0;
51 
52 int main()
53 {
54     MYFILE();
55     n = read(); scanf("%s", s);
56     for(int i = 0; i < (1 << n); ++i)
57     {
58         s1.clear(); s2.clear();
59         for(int j = 0; j < n; ++j)
60             if(i & (1 << j)) s1 += s[j];
61             else s2 += s[j];
62             reverse(s2.begin(), s2.end());
63             mp[s1 + "#" + s2]++;    //加一个字符,区分s1, s2 
64     }
65     for(int i = 0; i < (1 << n); ++i)
66     {
67         s1.clear(); s2.clear();
68         for(int j = 0; j < n; ++j)
69             if(i & (1 << j)) s1 += s[j + n];
70             else s2 += s[j + n];
71             reverse(s2.begin(), s2.end());
72             ans += mp[s2 + "#" + s1];
73     }    
74     write(ans >> 1); enter;
75     return 0;
76 }
View Code

 

posted @ 2018-10-04 23:19  mrclr  阅读(196)  评论(0编辑  收藏  举报