[考试]20151105

守給你的承諾、 提交于 2019-12-20 03:29:37

控制面板

1、ZJOI2012 灾难

TAG:拓扑排序+倍增LCA

状态:100%

 

2、巡防

TAG:树上最远点对DP做法

状态:90%

 

3、完美代价

TAG:贪心

状态:100%

 

4、cards

TAG:未知

状态:100%

 

5、set

TAG:动态规划

状态:100%

 

6、chessboard

TAG:贪心

状态:75%

 

7、HCN

TAG:搜索

状态:20%

 

8、seq

TAG:动态规划

状态:100%

 

1、ZJOI2012 灾难

  主体思路:求出拓扑序,对于每个节点根据联通关系重新构建一棵树,直接用dep数组体现。根据这棵树直接求出以每个节点为根节点的子树大小,即答案。中间用到倍增LCA来求得这棵树。

  首先我们自己新建一个超级生产者0号节点,所有生产者都以这个超级生产者为食物。 考虑在所有生物中建立这样一个结构: 这个结构是一颗树,一个节点代表一种生物,树上一个节点满足这个灭绝会且仅会导致以它 为根的子树灭绝。 如果我们能够建立这样一个结构,那么每个生物对应的答案可以一边 dfs 统计子树大小就得出。 

  下面就介绍如何建立这样的结构: 首先,把所有的点按照从猎物到捕食者的顺序拓扑排序。然后依次考虑每一个生物 p,假设 在这之前已经建好了拓扑排序顺序在 p 之前的所有生物组成的上面描述的树结构。假设 p 的事物有 k 种,分别是 food[1]…food[k](由于拓扑排序,这些点都已经在树结构中)。那么 只有 food[1]…food[k]这 k 个点的 lca 到根的路径上的点会导致 p 灭绝。于是,就可以把 p 作 为 food[1]…food[k]这 k 个点的 lca 的儿子。 具体实现上,lca 用倍增维护即可,每次新加一个点,都可以一遍倍增把新加的点的倍增数 组得到。

 

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <vector>
 4 using namespace std;
 5 
 6 #define MAXN 70005
 7 #define pb push_back
 8 
 9 int n, x, list[MAXN], f[MAXN][16], dep[MAXN], ans[MAXN], cnt[MAXN], tot; 
10 
11 vector <int> edge[MAXN], edge2[MAXN];
12 
13 int LCA(int x, int y)
14 {
15     int o;
16     if (dep[x] < dep[y]) swap(x, y);
17     for (o = 0; (1 << o) <= dep[x]; o++); o--;
18     for (int i = o; i >= 0; i--)
19         if (dep[x] - (1 << i) >= dep[y]) x = f[x][i];
20     if (x == y) return x;
21     for (int i = o; i >= 0; i--)
22         if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
23     return f[x][0];
24 }
25 
26 int main()
27 {
28     freopen("catas.in", "r", stdin);
29     freopen("catas.out", "w", stdout);
30     scanf("%d", &n);
31     for (int i = 1; i <= n; i++)
32     {
33         int lf = 1;
34         scanf("%d", &x);
35         while (x) edge[i].pb(x), edge2[x].pb(i), scanf("%d", &x), lf = 0;
36         if (lf) edge[i].pb(0), edge2[0].pb(i);
37     }
38     for (int i = 1; i <= n; i++) cnt[i] = edge[i].size();
39     for (int i = 0; i <= n; i++)
40     {
41         int o = list[i];
42         for (int x = 0; x < edge2[o].size(); x++)
43         {
44             int v = edge2[o][x];
45             if (!--cnt[v]) list[++tot] = v;
46         }
47     }
48     for (int i = 1; i <= n; i++)
49     {
50         int o = list[i], lca = -1;
51         for (int x = 0; x < edge[o].size(); x++)
52         {
53             int v = edge[o][x];
54             lca = lca == -1 ? v : LCA(lca, v);
55         }
56         dep[o] = dep[lca] + 1, f[o][0] = lca;
57         for (int j = 1; j <= 15; j++) f[o][j] = f[f[o][j - 1]][j - 1];
58     }
59     for (int i = 0; i <= n; i++) ans[i] = 1;
60     for (int i = n; i >= 0; i--) ans[f[list[i]][0]] += ans[list[i]];
61     for (int i = 1; i <= n; i++) printf("%d\n", ans[i] - 1);
62     return 0;
63 }

 

 

 

 

2、巡防

  首先看如果要求从某村庄出发访问所有的村庄最后还要回来 那答案显然是所有边权之 和乘以 2(每条边都是一去一回) 现在不要求回来的话就是省掉了从某个点回到起点的这么条路径,显然这个路径越长越 靠谱,所以从某点出发的答案就是边权和乘 2 减去这个点到它最远点的距离。 然后就是树上的最远点对。这就化归到了一个经典模型上。 

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #define MAXN 100005
 4 
 5 int max(int a, int b) { return a > b ? a : b; }
 6 
 7 struct Edge { int v, next, w; } edge[MAXN << 1];
 8 
 9 int now, h[MAXN], n, u, v, w, dis[MAXN], vis[MAXN], maxw, maxp, f[MAXN], ans, sum, get[MAXN];
10 
11 void addEdge(int u, int v, int w) { now++, edge[now] = (Edge) {v, h[u], w}, h[u] = now; }
12 
13 void DFS(int o, int w)
14 {
15     if (w > maxw) maxw = w, maxp = o;
16     for (int x = h[o]; x; x = edge[x].next)
17     {
18         int v = edge[x].v;
19         if (!vis[v]) vis[v] = 1, get[v] = 1, DFS(v, w + edge[x].w), vis[v] = 0;
20     }
21 }
22 
23 void DFS2(int o, int w)
24 {
25     dis[o] = max(dis[o], w);
26     if (w > maxw) maxw = w, maxp = o;
27     for (int x = h[o]; x; x = edge[x].next)
28     {
29         int v = edge[x].v;
30         if (!vis[v]) vis[v] = 1, DFS2(v, w + edge[x].w), vis[v] = 0;
31     }
32 }
33 
34 int main()
35 {
36     freopen("path.in", "r", stdin);
37     freopen("path.out", "w", stdout);
38     scanf("%d", &n);
39     for (int i = 1; i <= n - 1; i++) 
40         scanf("%d %d %d", &u, &v, &w), addEdge(u, v, w), addEdge(v, u, w), sum += w << 1;
41     for (int i = 1; i <= n; i++) scanf("%d", &f[i]);
42     for (int i = 1; i <= n; i++) if (f[i]) { get[i] = 1, DFS(i, 0); break; }
43     for (int i = 1; i <= n; i++) if (!get[i]) printf("-1"), exit(0);
44     DFS2(maxp, 0), DFS2(maxp, 0);
45     for (int i = 1; i <= n; i++) ans = (dis[i] > ans && f[i]) ? dis[i] : ans;
46     printf("%d", sum - ans);
47     return 0;
48 }

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define MAXN 200005
 5 using namespace std;
 6 int n;
 7 int totaledge,point[MAXN],v[MAXN],w[MAXN],next[MAXN];
 8 int f[MAXN][2];
 9 int g[MAXN][2];
10 int ans[MAXN];
11 void addedge(int x,int y,int z){
12     totaledge++;
13     v[totaledge] = y;
14     w[totaledge] = z;
15     next[totaledge] = point[x];
16     point[x] = totaledge;
17 }
18 void dfs1(int fa, int x){
19     int i,j;
20     for (i=point[x];i;i=next[i]){
21         if (v[i] == fa) continue;
22         dfs1(x, v[i]);
23         j = f[v[i]][0] + w[i];
24         if (j>f[x][0]){
25             f[x][1] = f[x][0];
26             g[x][1] = g[x][1];
27             f[x][0] = j;
28             g[x][0] = v[i];
29         }
30         else if (j>f[x][1]){
31             f[x][1] = j;
32             g[x][1] = v[i];
33         }
34     }
35 }
36 void dfs2(int fa, int x,int y){
37     int i,j;
38     ans[x] = max(f[x][0],y);
39     for (i=point[x];i;i=next[i]){
40         if (v[i] == fa) continue;
41         if (g[x][0]!=v[i]) dfs2(x, v[i],max(y,f[x][0])+w[i]);
42             else dfs2(x, v[i],max(y,f[x][1])+w[i]);
43     }
44 }
45 int d[MAXN];
46 char str[20];
47 int main(){
48     int i,x,y,z, t, ri;
49 //    for (ri = 0; ri < 30; ri++){
50 //        sprintf(str, "%d.in", ri);
51        freopen("path.in", "r", stdin);
52 //        sprintf(str, "%d.out", ri);
53        freopen("path.out", "w", stdout);
54         scanf("%d",&n);
55         memset(point,0,sizeof(point));
56         totaledge = t = 0;
57         for (i=2;i<=n;i++){
58             scanf("%d%d%d",&x,&y, &z);
59             addedge(x,y,z);
60             addedge(y,x,z);
61             t += z + z;
62         }
63         z = 0;
64         for (i = 1; i <= n; i++){
65             scanf("%d", &d[i]);
66             z += d[i];
67         }
68         if (z == 0){
69             printf("-1\n");
70             return 0;
71         }
72         dfs1(0, 1);
73         dfs2(0, 1, 0);
74         z = 0;
75         for (i=1;i<=n;i++){
76             if (d[i]) z = max(z, ans[i]);
77         }
78         printf("%d\n", t - z);
79 //    }
80     return 0;
81 }

 

3、完美代价

注意到这个问题可以这样考虑: 假设相同字符之间也是有区别的。那么字符串可以看做一个排列。如果答案是已知的一 个排列 P,则任务就是求从 (1,2,...,n) 变换到排列 P 需要多少次操作。
很明显,最优的方案要求每次使逆序对个数 +1 。很明显,一种最优的方法就是一开始就 将排列的第一个位置和最后一个位置放好,之后再对剩下的部分做变换。那么相对应的,也 有一种方法是最后才将排列的第一个位置和最后一个位置弄好。考虑使用这种方法。 假设现在要处理字符串的第一个字符和最后一个字符,他们分别为 A 和 B,并且我们用 C 来替代。由于 C 一定要移动到和 A, B 相邻的位置,所以如下图:

+-------+

|           |

|           |

AC ... CB
明显,如果 C ≠ A,B ,则交换 AC 和 CB 后方案是不合法的,因此 C 一定是 A,B 中的 一个。
那么再回到一开始的先处理头尾的情况。容易知道 C 要和 A, B 相等,因此我们只需要 找到离 A 最近的 B 以及离 B 最近的 A,判断哪个更近即可。

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 char s[10010];
 8 int n, cnt;
 9 
10 int main()  {
11     freopen("reference.in","r",stdin);  freopen("reference.out","w",stdout); 
12   scanf("%d", &n);
13   scanf("%s", s + 1);
14   
15   int x = 0, y = n + 1;
16   while (++x < --y)  {
17     if (s[x] == s[y])
18       continue;
19     int nx = x + 1, ny = y - 1;
20     while (s[nx] != s[y])
21       ++nx;
22     while (s[ny] != s[x])
23       --ny;
24     if (nx == y  &&  ny == x)  {
25       printf("Impossible");
26       return 0;
27     }
28     if (nx - x <= y - ny  &&  nx != y)
29       for (int i = nx; i > x; --i)
30     swap(s[i], s[i - 1]), ++cnt;
31     else
32       for (int i = ny; i < y; ++i)
33     swap(s[i], s[i + 1]), ++cnt;
34   }
35   printf("%d\n", cnt);
36 }

 

 1 #include <cstdio>
 2 #define MAXN 8005
 3 
 4 int n, tot[30], totx, x;
 5 char ch[MAXN];
 6 
 7 int work()
 8 {
 9     int res = 0;
10     for (int i = 0; i <= (n >> 1); i++)
11         if (ch[i] == x)
12            { 
13             for (int j = i; j < n - i - 1; j++) 
14                 if (ch[n - i - 1] == ch[j]) 
15                 {
16                     res += j - i; 
17                     for (int k = j; k > i; k--) ch[k] = ch[k - 1];
18                     ch[i] = ch[n - i - 1]; 
19                     break; 
20                 }
21            }
22            else 
23            { 
24                for (int j = n - i - 1; j >= i; j--) 
25                    if (ch[i] == ch[j]) 
26                 { 
27                     res += n - i - 1 - j; 
28                        for (int k = j; k < n - i - 1; k++) ch[k] = ch[k + 1]; 
29                        ch[n - i - 1] = ch[i]; 
30                        break; 
31                 }
32            }
33     return res;
34 }
35 
36 int main()
37 {
38     freopen("reference.in", "r", stdin);
39     freopen("reference.out", "w", stdout);
40     scanf("%d %s", &n, ch);
41     for (int i = 0; i <= n - 1; i++) tot[ch[i] - 'a']++;
42     for (int i = 0; i <= 25; i++) if (tot[i] % 2) totx++, x = i + 'a';
43     if (totx >= 2) printf("Impossible"); else printf("%d", work());
44     return 0;
45 } 

 

4、cards

假设最后 xxxxxx..xxxxxx 被分成了 y 块 那么要在其中找 y-1 个位置插入 o 然后我们发现这 y-1 个位置会决定减去的分数 而在 y-1 个位置怎么放 o 会决定加上的分数 这两者是独立的
考虑怎么找这 y-1 个位置 显然是越平均越好 比如说将 4 分成两份 1+3*3>2*2+2*2
在 y-1 个位置怎么放呢 显然是 y-2 个位置中放一个 o 剩下一个位置放剩余的 o 和放 x 的原理是一样的
然后需要注意下 y=1 的情况
枚举 y 即可

 

 1 #include <cstdio>
 2 #define INF 1ll << 60
 3 
 4 typedef long long ll;
 5 
 6 ll a, b, ans = -INF, x;
 7 
 8 ll get(ll o)
 9 {    
10     ll s = b / (o + 1), v = b % (o + 1);
11     ll f1 = v * (s + 1) * (s + 1) + (o + 1 - v) * s * s, f2 = o ? (a - o + 1) * (a - o + 1) + o - 1 : (a - o) * (a - o) + o;
12     return f2 - f1;
13 }
14 
15 int main()
16 {
17     freopen("cards.in", "r", stdin);
18     freopen("cards.out", "w", stdout);
19     scanf("%d %d", &a, &b);
20     for (ll i = 0; i <= a; i++) 
21     {
22         ll o = get(i);
23         if (o > ans) ans = o, x = i;
24     }
25     printf("%I64d\n", ans);
26     ll s = b / (x + 1), v = b % (x + 1);
27     if (!x)
28     {
29         for (ll i = 1; i <= b; i++) printf("x");
30         for (ll i = 1; i <= a; i++) printf("o");
31     }
32     else
33     {
34         for (ll i = 1; i <= v; i++)
35         {
36             for (ll j = 1; j <= s + 1; j++) printf("x");
37             printf("o");
38         }
39         for (ll i = 1; i <= x - v; i++)
40         {
41             for (ll j = 1; j <= s; j++) printf("x");
42             printf("o");
43         }
44         for (ll i = 1; i <= a - x; i++) printf("o");
45         for (ll i = 1; i <= s; i++) printf("x");
46     }
47     return 0;
48 }

 

 

 

 

5、set

注意到我们计数的状态有对成型,即:一个A胜利的局面恰好唯一对应一个B胜利的局面(双射为交换双方状态)。

那么,我们只需要考虑AB平局的状态,用总状态数减去平局状态并除以2就能得到A胜利的状态数。

由于异或有自反性,所以AB平局的局面两者得到的权值的异或值为0.

以f[i][j]表示第i个数,现在两者得到的权值的异或值为j的方案数:

f[i][j] = f[i - 1][j] + f[i - 1][j ^ a[i]] + f[i - 1][j ^ a[i]]

则答案为1 / 2 * (3的n次方 - f[n][0])。

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 #define ll long long
 6 #define FOR( A, B, C ) for ( int A = B, _end_ = C; A <= _end_; A++ )
 7 
 8 int f[ 4096 ][ 4096 ], n, a[ 4096 ], P = 1998585857;
 9 
10 int fpm( int F, int FF, int P ) {
11     int ans = 1;
12     for ( ; FF; FF >>= 1, F = (ll)F * F % P )
13         if ( FF & 1 ) ans = (ll)ans * F % P;
14     return ans;
15 }
16 
17 int main() {
18     freopen( "set&set.in", "r", stdin );
19     freopen( "set&set.out", "w", stdout );
20     
21     scanf( "%d", &n );
22     FOR ( i, 1, n ) { scanf( "%d", &a[ i ] ); }
23     
24     f[ 0 ][ 0 ] = 1;
25     FOR ( i, 1, n ) {
26         FOR ( j, 0, 4095 ) {
27             f[ i ][ j ] = ( f[ i - 1 ][ j ] + ( (ll)f[ i - 1 ][ j ^ a[ i ] ] << 1 ) ) % P;
28         }
29     }
30     
31     int ans = ( (ll)fpm( 3, n, P ) - f[ n ][ 0 ] + P ) % P; ans = (ll)ans * fpm( 2, P - 2, P ) % P;
32     
33     printf( "%d\n", ans );
34 }

 

6、chessboard

其实这个问题可以用贪心能解决,以横轴为例,假设扫描到了i,将左端点<=i的区间都加入一个集合,然后在已经加入的区间集合中选取一个右端点最小的区间来作为i的匹配区间即可。用堆优化或者set。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <set>
 4 using namespace std;
 5 
 6 #define MAXN 100005
 7 
 8 int n, a[MAXN], b[MAXN], c[MAXN], d[MAXN], ansx[MAXN], ansy[MAXN], h[MAXN], next[MAXN], md;
 9 
10 struct cmp
11 {
12     bool operator () (int x, int y)
13     {
14         return md ? (d[x] < d[y] || (d[x] == d[y] && x < y)) : (c[x] < c[y] || (c[x] == c[y] && x < y));
15     }
16 };
17 
18 set <int, cmp> st;
19 
20 bool work1()
21 {
22     int get = 0; md = 0;
23     st.clear(), memset(h, 0, sizeof(h));
24     for (int i = 1; i <= n; i++) 
25         next[i] = h[a[i]], h[a[i]] = i;
26     for (int i = 1; i <= n; i++)
27     {
28         for (int x = h[i]; x; x = next[x]) st.insert(x);
29         while (!st.empty())
30         {
31             int o = *st.begin(); st.erase(o);
32             if (c[o] < i) continue;
33             ansx[o] = i, get = 1;
34             break;
35         }
36         if (!get) return 0;
37     }
38     return 1;
39 }
40 
41 bool work2()
42 {
43     int get = 0; md = 1;
44     st.clear(), memset(h, 0, sizeof(h));
45     for (int i = 1; i <= n; i++) 
46         next[i] = h[b[i]], h[b[i]] = i;
47     for (int i = 1; i <= n; i++)
48     {
49         for (int x = h[i]; x; x = next[x]) st.insert(x);
50         while (!st.empty())
51         {
52             int o = *st.begin(); st.erase(o);
53             if (d[o] < i) continue;
54             ansy[o] = i, get = 1;
55             break;
56         }
57         if (!get) return 0;
58     }
59     return 1;
60 }
61 
62 int main()
63 {
64     freopen("chessboard.in", "r", stdin);
65     freopen("chessboard.out", "w", stdout);
66     scanf("%d", &n);
67     for (int i = 1; i <= n; i++) 
68         scanf("%d %d %d %d", &a[i], &b[i], &c[i], &d[i]);
69     if (work1() && work2()) 
70         for (int i = 1; i <= n; i++) printf("%d %d\n", ansx[i], ansy[i]); 
71     else printf("%d", -1);
72     return 0;
73 }

 

7、HCN

大搜索题。首先,对于一个数a,由唯一分解定律一定可以分为a = p1 ^ c1 * p2 ^ c2 * ...* pn ^ cn,且其因子个数为。第一步,对于一组给定的c,显然我们应该把较大的ci分给较小的质数,这样可以得到更小的a。从而应该把c由大到小分配给前n个质数。

只是这样写就能解决n <= 10 ^ 18的部分了。接下来的部分就要写高精度了。

为了降低计算量,我们考虑加入最优化剪枝:

1、当c1确定时,我们在考虑当前第i个素数的指数时,存在一个上界c1 / ki,其中ki表示最大的满足2 ^ ki <= pri[i]的数,当ci比其更大时,不如ci -= 1,c1 += ki。

2、同样地方法,我们也能得到它的一个上界(c1 - 2 * ki - 1) / (1 + ki),表示ci比其更小时,不如c1 -= ki,ci += 1

3、以上两个剪枝的关键都在于没有办法给2进行剪枝,所以我们可以考虑如何限制住c1.我们可以考虑用满足来进行对一个素数的剪枝。令ti表示最大的满足pri[i]^ti <= pri[n + 1]的数,当ci > 2 * ti时,不如把ci -= ti,c[n + 1] += 1。

呕。

 

  1 #include <cstdio>
  2 #include <cstring>
  3 
  4 #define MAX 30
  5 #define MAXX 205
  6 #define LEN 100000000
  7 
  8 typedef long long ll;
  9 
 10 int max1(int a, int b) { return a > b ? a : b; }
 11 
 12 int min1(int a, int b) { return a < b ? a : b; }
 13 
 14 ll max;
 15 
 16 int pri[MAXX], ptop, top, s[MAXX], dig = LEN, q[MAXX], k[MAXX];
 17 
 18 struct ubInt
 19 {
 20     ll num[MAX + 1];
 21     int fir;
 22     
 23     void init(int x) { num[fir = MAX] = x; }
 24     
 25     void clear() { for (int i = fir; i <= MAX; i++) num[i] = 0;    fir = MAX; }
 26     
 27     void exceed() 
 28     {
 29         int i = MAX, j = 0;
 30         for (; i >= fir || j; i--) 
 31         {
 32             num[i] += j, j = 0;
 33             if (num[i] >= dig) j = num[i] / dig, num[i] %= dig;
 34         }
 35         fir = i + 1;
 36     }
 37     
 38     void operator *= (const int x) 
 39     {
 40         for (int i = fir; i <= MAX; i++) num[i] *= x;
 41         exceed();
 42     }
 43     
 44     void operator = (const ubInt &x) 
 45     {
 46         clear();
 47         for (int i = x.fir; i <= MAX; i++) num[i] = x.num[i];
 48         fir = x.fir;
 49     }
 50     
 51     void read() 
 52     {
 53         char ch[MAXX]; int l;
 54         scanf("%s", ch), l = strlen(ch);
 55         int j = 1; fir = MAX;
 56         for (int i = l - 1; i >= 0; i--)
 57         {
 58             num[fir] += (ch[i] - '0') * j, j *= 10;
 59             if (j == dig) j = 1, fir--;
 60         }
 61         if (j == 1) fir++;
 62     }
 63     
 64     void print() 
 65     {
 66         printf("%I64d", num[fir]);
 67         for (int i = fir + 1; i <= MAX; i++) printf("%08I64d", num[i]);
 68         printf("\n");
 69     }
 70 };
 71 
 72 ubInt ans, n, now[MAXX + 10];
 73 
 74 int operator < (const ubInt &a, const ubInt &b)
 75 {
 76     if (a.fir != b.fir) return a.fir > b.fir;
 77     for (int i = a.fir; i <= MAX; i++)
 78         if (a.num[i] != b.num[i]) return a.num[i] < b.num[i];
 79     return 0;
 80 }
 81 
 82 void init() {
 83 #define MMM 300
 84     int t[MMM + 10];
 85     max = 1, ans.init(1);
 86     for (int i = 1; i <= MAX; i++) now[i].fir = MAX;
 87     now[1].num[MAX] = 1;
 88     for (int i = 2; i <= MMM; i++) t[i] = 0;
 89     for (int i = 2; i <= MMM; i++)
 90         if (!t[i]) 
 91         {
 92             pri[++ptop] = i;
 93             for (int j = 2; j * pri[ptop] <= MMM; j++) t[j * pri[ptop]] = 1;
 94         }
 95     ubInt e; e.fir = 0, e.clear(), e.init(1);
 96     for (int i = 1; i <= ptop; i++)
 97     {
 98         e *= pri[i];
 99         if (n < e) { top = i - 1; break; }
100     }
101     for (int i = 1; i <= top; i++)
102     {
103         int tmp = 1;
104         for (int j = 0; j <= 100; j++)
105         {
106             if (tmp > pri[top]) { s[i] = j - 1; break; }
107             tmp *= pri[i];
108         }
109     }
110     for (int i = 1; i <= top; i++)
111     {
112         int tmp = 1;
113         for (int j = 0; j <= 100; j++)
114         {
115             if (tmp > pri[i]) { k[i] = j - 1; break; }
116             tmp <<= 1;
117         }
118     }
119 }
120 
121 void brute(int v, ll get, int r3) 
122 {
123     if (n < now[v]) return;
124     else if (get > max  ||  (get == max  &&  now[v] < ans)) max = get, ans = now[v];
125     if (v > top) return;
126     int l = 1, r1 = 1 << 30, r2;
127     if (v != 1) l = (q[1] - (k[v] << 1) - 1) / (1 + k[v]) + 1, r1 = q[1] / k[v];
128     r2 = s[v] << 1 | 1;
129     now[v + 1] = now[v];
130     for (int i = 1; i <= max1(l, 1) - 1; i++) now[v + 1] *= pri[v];
131     for (int i = max1(l, 1); i <= min1(min1(r1, r2), r3); i++)
132         now[v + 1] *= pri[v], q[v] = i, brute(v + 1, get * (i + 1), i);
133 }
134 
135 int main() 
136 {
137     freopen("HCN.in", "r", stdin);
138     freopen("HCN.out", "w", stdout);
139     n.read(), init(), brute(1, 1, 100), ans.print();
140     return 0;
141 }

 

 

 

 

8、seq

考虑已经有了一个子序列 我现在要在后面加入一个数 a[i] 如果能加说明不存在二元组(s,t)满足 a[s]<a[t]<a[i] 二元组有很多我们需要关心的其实只是最小的 a[t]
那么加进去一个数字以后最小的 a[t]可能发生改变 那么如何判断加进去的 a[i]能否成为最小的 a[t]呢 只需要知道没加进 a[i]时的子序列的最小值
那么 dp 的思路就建立了
令 f[i][j][l]表示考虑了前 i 个数 子序列中最小值为 j 子序列中最小的 a[t]为 l

 

 1 #include <cstdio>
 2 #define MAXN 205
 3 #define MOD 1000000007
 4 
 5 int n, a[MAXN], f[MAXN][MAXN][MAXN], ans;
 6 
 7 void upd(int &a, int b) { (a += b) %= MOD; }
 8 
 9 int main()
10 {
11     freopen("seq.in", "r", stdin);
12     freopen("seq.out", "w", stdout);
13     scanf("%d", &n);
14     for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
15     f[0][n + 1][n + 1] = 1;
16     for (int i = 0; i < n; i++)
17         for (int j = 1; j <= n + 1; j++)
18               for (int l = 1; l <= n + 1; l++)
19                 if (f[i][j][l])
20                 {
21                       upd(f[i + 1][j][l], f[i][j][l]);
22                       if (a[i + 1] > l) continue;
23                       if (a[i + 1] > j) upd(f[i + 1][j][a[i + 1]], f[i][j][l]);
24                       if (a[i + 1] < j) upd(f[i + 1][a[i + 1]][l], f[i][j][l]);
25                 }
26     for (int j = 1; j <= n; j++)
27         for(int l = 1; l <= n + 1; l++) upd(ans, f[n][j][l]);
28     printf("%d", ans);
29     return 0;
30 }

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!