NOIP模拟64

T1:

  考场上以为是数论题,想推性质,然而并不会,于是考虑60分

考虑发现数据范围中k <= 40,考虑模数很小,那么考虑计数问题的经典

解决方法——映射,考虑在1~k范围内求解,在反射回原值域,范围可以通过

不等式限制

  正解并不是数论,而是数据结构维护,考虑类似排列,我们通过枚举

一维来解决限制,便于维护,通常以枚举中间量,可以限制出左右两侧变量

于是枚举b,发现一个很优秀的性质为a为一次,于是a + b^2的范围即可确定

,那么考虑c的贡献(即落到多少区间内),边更新边计算即可,树状数组

区间修改单点查询

代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I long long
 4 #define C char
 5 #define B bool
 6 #define V void
 7 #define D double
 8 #define LL long long
 9 #define UI unsigned int
10 #define UL unsigned long long
11 #define P pair
12 #define MP make_pair
13 #define a first
14 #define b second
15 #define debug cout << "It's Ok Here !" << endl;
16 #define lowbit(x) (x & -x)
17 #define FP(x) freopen (#x,"r",stdin)
18 #define FC(x) freopen (#x,"w",stdout)
19 const I N = 1e5 + 3;
20 I T,n,t;
21 inline I read () {
22     I x(0),y(1); C z(getchar());
23     while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
24     while ( isdigit(z))  x = x * 10 + (z ^ 48), z = getchar();
25     return x * y;
26 }
27 inline V Max (I &a,I b) { a = a > b ? a : b; }
28 inline V Min (I &a,I b) { a = a < b ? a : b; }
29 inline I max (I a,I b) { return a > b ? a : b; }
30 inline I min (I a,I b) { return a < b ? a : b; }
31 inline V swap (I &a,I &b) { a ^= b, b ^= a, a ^= b; }
32 inline I abs (I a) { return a >= 0 ? a : -a; }
33 inline P<I,I> operator + (const P<I,I> &a,const P<I,I> &b) {
34     return MP (a.a + b.a,a.b + b.b);
35 }
36 inline P<I,I> operator - (const P<I,I> &a,const P<I,I> &b) {
37     return MP (a.a - b.a,a.b - b.b);
38 }
39 struct BIT {
40     I c[N];
41     inline V secmod (I x,I y) { 
42         for (;x <= t;x += lowbit (x))
43             c[x] ++ ;
44         for (;y <= t;y += lowbit (y))
45             c[y] -- ;
46     }
47     inline I preque (I x) { I ans(0);
48         for (; x ;x -= lowbit (x))
49             ans += c[x];
50         return ans;
51     }
52 }B1;    
53 signed main () {
54     FP (exclaim.in), FC (exclaim.out);
55     T = read();
56     for (I i(1);i <= T; ++ i) {
57         printf ("Case %d: ",i);
58         n = read(), t = read(); I tmp(0); LL ans(0);
59         for (I j(1);j <= n; ++ j) {
60             I l((j * j + 1) % t + 1), 
61               r((j * j + j) % t + 1);
62             if (j >= t) tmp += j / t - (r + 1 == l) - (r - l + 1 == t);
63             if (l <= r) B1.secmod (l,r + 1);
64             if (l >  r) B1.secmod (1,r + 1), B1.secmod (l,t + 1);
65             I last (ans);
66             ans += tmp + B1.preque (j * j * j % t + 1);
67         }
68         printf ("%lld\n",ans);
69         memset (B1.c,0,sizeof B1.c);
70     }
71 }
View Code

T2:

  首先看错题目暴毙,要求循环同构,于是首先简单的想法为n^2枚举区间

那么考虑如何O(1)判断循环同构,字符串匹配一种常见的方法显然为哈希

然而并不能应对循环,考虑剪枝,这里应用到字符集优化,即通过字符集哈希

首先判断两端字符是否相等,在进行循环判断,可以通过此题,再通过map记录

可以应对Hack数据

代码如下:

 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 D double
 8 #define LL long long
 9 #define UI unsigned int
10 #define UL unsigned long long
11 #define P pair
12 #define MP make_pair
13 #define a first
14 #define b second
15 #define debug cout << "It's Ok Here !" << endl;
16 #define lowbit(x) (x & -x)
17 #define FP(x) freopen (#x,"r",stdin)
18 #define FC(x) freopen (#x,"w",stdout)
19 const I N = 5e3 + 3;
20 I n,ans;
21 UL p[N],f[N],t[N];
22 inline I read () {
23     I x(0),y(1); C z(getchar());
24     while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
25     while ( isdigit(z))  x = x * 10 + (z ^ 48), z = getchar();
26     return x * y;
27 }
28 inline V Max (I &a,I b) { a = a > b ? a : b; }
29 inline V Min (I &a,I b) { a = a < b ? a : b; }
30 inline I max (I a,I b) { return a > b ? a : b; }
31 inline I min (I a,I b) { return a < b ? a : b; }
32 inline V swap (I &a,I &b) { a ^= b, b ^= a, a ^= b; }
33 inline I abs (I a) { return a >= 0 ? a : -a; }
34 inline P<I,I> operator + (const P<I,I> &a,const P<I,I> &b) {
35     return MP (a.a + b.a,a.b + b.b);
36 }
37 inline P<I,I> operator - (const P<I,I> &a,const P<I,I> &b) {
38     return MP (a.a - b.a,a.b - b.b);
39 }
40 inline B Check (I l,I r) {
41     I mid (l + r >> 1), len (r - l + 1 >> 1);
42     if (t[r] - t[mid] != t[mid] - t[l - 1]) return false;
43     UL com (f[r] - f[mid] * p[len]);
44     for (I i(l);i <= mid; ++ i) 
45         if ((f[mid] - f[i] * p[mid - i] - f[l - 1]) * p[i - l + 1] + f[i] == com)
46             return true;
47     return false;
48 }
49 signed main () {
50     FP (s.in), FC (s.out);
51     n = read(); p[0] = 1; 
52     for (I i(1);i <= 5000; ++ i)
53         p[i] = p[i - 1] * 13331;
54     for (I i(1),x,y;i <= n; ++ i) {
55         x = read();
56         t[i] = t[i - 1] + p[x];
57         f[i] = f[i - 1] * 13331 + x;
58         if (x == y) ans ++ ;  y = x;
59     }
60     for (I len(3);len <  n;len += 2) 
61       for (I i(1);i + len <= n; ++ i) {
62         if (Check (i,i + len)) ans ++ ;
63       }
64     printf ("%d\n",ans);
65 }
View Code

 T3:

  有向图求路径数,比较经典的矩阵乘法问题,分析矩阵乘法的性质我们可以发现

其可以视作图上路径问题,例如A[i][j] += A[i][k] * A[k][j],即从i到j的路径方案数=i-k * k-j

  于是考虑在矩阵视角分析此题,于是环就等价于主对角线上的位置,那么问题就等价于

求sigma (A^1 + A^2 + ... + A^(k - 1))主对角线上的值,等比数列显然可以分治解决

  然而时间复杂度O(n^3log^2n),并不太能过,于是发现可以直接预处理同底数幂降低时间复杂度

其实分析时间复杂度瓶颈很容易发现预处理解决的方法

代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I int
 4 #define C char
 5 #define V void
 6 #define FP(x) freopen (#x,"r",stdin)
 7 #define FC(x) freopen (#x,"w",stdout)
 8 #define N 100
 9 I n,k,mod,ans;
10 C s[N];
11 inline I Mod (I a) {return a > mod ? a - mod : a;}
12 struct MAT {
13     I A[N][N]; MAT () { memset (A,0,sizeof A); }
14 }A,Pre[20];
15 inline I read () {
16     I x(0); C z(getchar());
17     while (!isdigit(z)) z = getchar(); 
18     while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar();
19     return x;
20 }
21 inline V print (MAT A) {
22     for (I i(0);i < n; ++ i) {
23         for (I j(0);j < n; ++ j)
24             cout << A.A[i][j] << ' ';
25         cout << endl;
26     }
27     cout << endl;
28 }   
29 inline MAT operator * (const MAT &X,const MAT &Y) {
30     MAT Z; 
31     for (I i(0);i <  n; ++ i)
32         for (I k(0);k <  n; ++ k)
33             for (I j(0);j <  n; ++ j)
34                 Z.A[i][j] = Mod (Z.A[i][j] + 1ll * X.A[i][k] * Y.A[k][j] % mod);
35     return Z;
36 }
37 inline MAT operator ^ (MAT X,I t) {
38     MAT Z;
39     for (I i(0);i <  n; ++ i)
40         Z.A[i][i] = 1;
41     for (I i(0); t ;t >>= 1, ++ i)
42         if (t & 1) Z = Z * Pre[i];
43     return Z;
44 }
45 inline MAT operator + (const MAT &X,const MAT &Y) {
46     MAT Z; 
47     for (I i(0);i <  n; ++ i)
48         for (I j(0);j <  n; ++ j)
49             Z.A[i][j] = Mod (X.A[i][j] + Y.A[i][j]);
50     return Z;
51 }
52 MAT Divide (I x) {
53     I mid (x >> 1); MAT F (mid == 1 ? A : Divide (mid));
54     return x & 1 ? ((A ^ mid) * F) + F + (A ^ x) : ((A ^ mid) * F) + F;
55 }
56 signed main () {
57     FP (tour.in), FC (tour.out);
58     n = read();
59     for (I i(0);i <  n; ++ i) {
60         scanf ("%s",s);
61         for (I j(0);j <  n; ++ j)
62             A.A[i][j] = s[j] == 'Y';
63     }
64     k = read(), mod = read();
65     Pre[0] = A;
66     for (I i(1);i < 20; ++ i)
67         Pre[i] = Pre[i - 1] * Pre[i - 1];
68     MAT F (Divide (k - 1));
69     for (I i(0);i <  n; ++ i)
70         ans = Mod (ans + F.A[i][i]);
71     printf ("%d\n",ans);
72 }   
View Code

 

posted @ 2021-09-29 20:32  HZOI_LYM  阅读(37)  评论(0编辑  收藏  举报