BZOJ_4386

今天下午BZ在维护,我就用同学拿到的数据(权限号好爽)跑过了,等会晚上再交吧。这道题一看就是矩阵快速幂,最开始没有想到拆点的做法只想到了拆边,复杂度直接爆掉了。所以我们把每个点拆成3个,分别表示长度为1,2,3的边该连到这个点的哪个分身(不想画图了以后自己忘了自己YY吧)。然后开始矩阵快速幂,里面可以顺便处理出长度为L时,0~L的总方案数。最开始想到的比较蠢的做法是二分。强加了一个LOG,肯定是过不了的(这道题有56个点),所以我们预处理出2的logk*3次幂的矩阵,然后像倍增一样倒着加进去。

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstdlib>
 5 #include <cstring>
 6 #include <cmath>
 7 using namespace std;
 8 const int N = 43;
 9 const int M = 1003;
10 const double LOG = log(3*1000000000000000000LL);
11 struct rp {
12     long long w[N*3][N*3];
13 }need[64],ans;
14 long long st[3*N];
15 int n,m;
16 unsigned long long k;
17 bool flag = false;
18 inline rp cheng(rp a,rp b) {
19     rp c;
20     flag = false;
21     memset(c.w,0,sizeof(c.w));
22     for(int i = 1 ; i <= n*3+1 ; ++i)
23         for(int k = 1 ; k <= n*3+1 ; ++k)
24             if(!a.w[i][k])
25             for(int j = 1 ; j <= n*3+1 ; ++j) {
26                 c.w[i][j] += a.w[i][k]*b.w[k][j];
27                 if(c.w[i][j] < 0) {
28                     flag = true;
29                     return a;
30                 }
31             }
32     return c;
33 }
34 int main() {
35     scanf("%d%d",&n,&m);
36     cin >> k;
37     for(int i = 1 ; i <= m ; ++i) {
38         int x,y,d;
39         scanf("%d%d%d",&x,&y,&d);
40         if(d==1) need[0].w[y][x]++,st[y]++;
41         if(d==2) need[0].w[y+n][x]++,st[y+n]++;
42         if(d==3) need[0].w[y+2*n][x]++,st[y+2*n]++;
43     }
44     for(int i = 1 ; i <= n ; ++i) need[0].w[i][i+n]++;
45     for(int i = 1 ; i <= n ; ++i) need[0].w[i+n][i+2*n]++;
46     for(int i = 1 ; i <= n ; ++i) need[0].w[3*n+1][i] = 1;
47     for(int i = 1 ; i <= 3*n+1 ; ++i) ans.w[i][i] = 1;
48     need[0].w[3*n+1][3*n+1] = 1;
49     unsigned long long kk = 1;
50     int cnt = 0;
51     do {
52         kk<<=1;
53         if(kk>3*k) break;
54         cnt++;
55         need[cnt] = cheng(need[cnt-1],need[cnt-1]);
56         if(flag) {
57             cnt--;
58             break;
59         }
60     }while(1);
61     unsigned long long ans2 = 0;
62     for(int i = cnt ; i >= 0 ; --i) {
63         kk>>=1;
64         rp linshi = cheng(ans,need[i]);
65         if(flag) continue;
66         unsigned long long linshi2 = 0;
67         for(int i = 1 ; i <= 3*n+1 ; ++i) linshi2 += (linshi.w[3*n+1][i]*st[i]);
68         if(linshi2 < k) {
69             ans = linshi;
70             ans2 ^= kk;
71         }
72     }
73     if(ans2 > k*3) cout << -1;
74     else cout << ans2 + 1;
75 }
View Code

我跑得比唐宇豪慢啊慢啊。对了这道题开教会了我矩阵快速幂的常数优化,就是for i j k 的顺序换成for i k j然后判断第一个矩阵的i,k是不是为零,为零的话就直接continue掉。快了50倍呢。那个LOG不要管,是我判读爆没有的时候开始的乱搞。后来判断爆没有是看它是不是负数,还是比较靠谱。

原文地址:https://www.cnblogs.com/registerzxr/p/5130130.html