ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NOIP模拟48

2021-08-27 08:02:09  阅读:162  来源: 互联网

标签:return NOIP 48 ++ LL 考虑 getchar 模拟 define


T1:

  考虑先将完全图转化为数学语言,即完全状态(全排列)

容易发现m条删边实际上是限制条件,那么利用容斥解决限制

条件的思路较容易想到,于是问题转化为容斥m条删边所损失

的环数。

  裸容斥,考虑首先完全图下容易想到合法环为(n-1)!/2,

考虑抽象为数学语言,每个点代表序列上的一个点,那么问题

实际转化为排列数,考虑正确性,完全状态下并不需要考虑排

列合法性(全部合法),那么由于要求成环并且顺序不能相同,

显然为项链数(圆排列/2)。

  考虑首先减去包含一条所删边,那么必然多减了同时包含

两条所删边的情况,归纳出奇减偶加。

  这里首先给出二项式反演与子集反演的特点,对于本题而

言首先m范围很小那么对于子集反演枚举状态可以承受,其次

对于环上节点数相同的状态显然具有不同结果,需要根据情况

判断,因此需要子集反演。

  二项式反演具有优秀的时间复杂度(通过组合数代替子集

枚举)然而其缺点在与必须满足同一组合具有相同的性质,即

同一组合的计算方式相同(这也是其可以枚举组合数的原因)

  对应的子集反演时间复杂度较劣(2^n),然而其优点在于

并不在意组合内的性质差异,类似本题,对于环上节点数相同的

状态具有不同的计算方式,而子集枚举就能很好的针对性计算。

  回到本题考虑如何计算包含x条所删边的环数,考虑首先将

状态x中每一条链缩为一个点(目的是降低复杂度),类似的问

题转化为存在多少链包含当前点集,那么根据之前的分析,答案

为(n-|s|-1)!/2,然而不完全正确,考虑显然需要考虑将之前的缩点

造成的影响,发现假设存在一条竖直方向的链,显然从上往下排

列与从下往上排列是不同的,然而缩点后其失去了原来的矢量性

所以设当前状态链数为k,则答案为(n-|s|-1)!*2^(k-1)(每条链都有

两种方向可选)

  发现问题似乎结束了,然而考虑状态定义:包含x条边的环数

显然并不是所有情况都能根据公式计算,需要删除不可能成环的

方案(因为公式中的参数显然未考虑不合法情况,只是单纯根据

边数计算),发现若存在点度数大于2或成环但环数大小不为n的

情况数为0(对于大小为n的环显然不可能满足上述条件),利用

并查集判断即可。

代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I int
 4 #define C char
 5 #define B bool
 6 #define V void
 7 #define LL long long
 8 #define P pair<I,I>
 9 #define MP make_pair
10 #define lowbit(x) (x & -x)
11 const I N = 1e7 + 3;
12 const I mod = 1e9 + 7;
13 I n,m,ans,num[1 << 20],J[N];
14 vector <P> edge;
15 inline I read() {
16     I x(0),y(1); C z(getchar());
17     while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
18     while ( isdigit(z))  x = x * 10 + (z ^ 48), z = getchar();
19     return x * y;
20 }
21 inline I qpow (I a,I b) { I res(1);
22     for (; b ;a = (LL)a * a % mod, b >>= 1)
23       if (b & 1) res = (LL)res * a % mod;
24     return res;
25 }
26 struct DJS {
27     I f[N],size[N],times[N],num;
28     B jud[N];
29     queue <I> q;
30     inline V initital () { for (I i(1);i <= n; ++ i) f[i] = i, size[i] = 1; }
31     I get (I x) { return x == f[x] ? x : f[x] = get(f[x]); }
32     inline I merge (I x,I y) {
33       I fx(get(x)), fy(get(y));
34       if (fx == fy) return size[fx];
35       if ( jud[fx]) jud[fx] = 0, num -- ;
36       if (!jud[fy]) jud[fy] = 1, num ++ ;   
37       f[fx] = fy; size[fy] += size[fx]; q.push(x), q.push(y); 
38       return 0;
39     }
40     inline V clear () { 
41       num = 0;
42       while (!q.empty ()) {
43         I x(q.front()); q.pop();
44         f[x] = x; size[x] = 1; 
45         times[x] = 0; jud[x] = 0;
46       } 
47     }
48 }DJS;
49 inline V solve (I s) {
50     DJS.clear (); I size; 
51     if (num[s] > n) return ;
52     for (I i(0);i < edge.size(); ++ i) if (s & 1 << i) {
53       size = DJS.merge (edge[i].first,edge[i].second);
54       if (++ DJS.times[edge[i].first]  > 2) return ;
55       if (++ DJS.times[edge[i].second] > 2) return ; 
56       if (size) if (size == n) { num[s] & 1 ? ans -- : ans ++ ; return ; } else return ;
57     }
58     I tmp = (LL)J[n - num[s] - 1] * (1 << -- DJS.num) % mod;
59     num[s] & 1 ? ans = (ans - tmp) % mod : ans = (ans + tmp) % mod;
60 }
61 signed main () {
62     //freopen ("data.in","r",stdin); freopen ("my.out","w",stdout);
63     n = read(), m = read(); DJS.initital ();
64     J[0] = 1; for (I i(1);i <= n; ++ i) J[i] = (LL)J[i - 1] * i % mod;
65     for (I i(1);i <= m; ++ i) {
66       I x(read()),y(read()); if (x == y) continue;
67       edge.push_back (MP(x,y));
68     } 
69     ans = (LL)J[n - 1] * qpow (2,mod - 2) % mod;
70     for (I i(1),tmp(1 << edge.size());i < tmp; ++ i) 
71      num[i] = num[i - lowbit(i)] + 1, solve (i);
72     printf ("%d\n",(ans + mod) % mod);      
73 }
View Code

T3:

  题目描述显然考虑二分答案转判定,考虑判定在与是否存在合

法方案满足块数小于m,考虑DP,因为只有两行,与是分类讨论分

别在1,2,(1,2)行放置矩形。

  那么定义DP[i]为放置到第i列所需最少画数,考虑如何转移,首

先若上一次在(1,2)放置,那么画数加一即可,考虑对于在1,2

放置,不断枚举回溯几次决策取最小值即可,实际上本题的本质是背

包DP变形。

  注意因为要保持最优性决策,预处理1,2,(1,2)每一块画

最多覆盖范围即可。

代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I int
 4 #define C char
 5 #define B bool
 6 #define LL long long
 7 const I N = 1e5 + 3;
 8 I n,m,nxt1[N],nxt2[N],nxt3[N],dp[N];
 9 LL s1[N],s2[N],s3[N];
10 inline I read() {
11     I x(0),y(1); C z(getchar());
12     while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
13     while ( isdigit(z))  x = x * 10 + (z ^ 48), z = getchar();
14     return x * y;
15 }
16 B check (LL x) {
17     for (I i(1);i <= n; ++ i) {
18       nxt1[i] = nxt1[i - 1], nxt2[i] = nxt2[i - 1], nxt3[i] = nxt3[i - 1];
19       while (s1[i] - s1[nxt1[i]] > x) nxt1[i] ++ ;
20       while (s2[i] - s2[nxt2[i]] > x) nxt2[i] ++ ;
21       while (s3[i] - s3[nxt3[i]] > x) nxt3[i] ++ ;
22     }
23     for (I i(1);i <= n; ++ i) {
24       I p1(i),p2(i),s(0);
25       dp[i] = dp[nxt3[i]] + 1;
26       while (++ s <= m && (p1 || p2)) {
27         p1 > p2 ? p1 = nxt1[p1] : p2 = nxt2[p2];
28         dp[i] = min(dp[i],dp[max (p1,p2)] + s);
29       }
30       if (dp[i] > m) return false;
31     }
32     return true;
33 }
34 signed main () {
35     I MA(INT_MIN); LL S(0);
36     n = read(), m = read();
37     for (I i(1),x;i <= n; ++ i) S += x = read(), s1[i] = s1[i - 1] + x, MA = max (MA,x);
38     for (I i(1),x;i <= n; ++ i) S += x = read(), s2[i] = s2[i - 1] + x, MA = max (MA,x);
39     for (I i(1);i <= n; ++ i) s3[i] = s1[i] + s2[i];
40     LL l(MA),r(S);
41     while (l < r) { LL mid(l + r >> 1);
42       check (mid) ? r = mid : l = mid + 1;
43     }
44     printf ("%lld\n",l);
45 }
View Code

 

标签:return,NOIP,48,++,LL,考虑,getchar,模拟,define
来源: https://www.cnblogs.com/HZOI-LYM/p/15192308.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有