HDU 4896 Minimal Spanning Tree(矩阵快速幂)

题意:


给你一幅这样子生成的图,求最小生成树的边权和。


思路:对于i >= 6的点连回去的5条边,打表知907^53 mod 2333333 = 1,所以x的循环节长度为54,所以9个点为一个循环,接下来的9个点连回去的边都是一样的。预处理出5个点的所有连通状态,总共只有52种,然后对于新增加一个点和前面点的连边状态可以处理出所有状态的转移。然后转移矩阵可以处理出来了,快速幂一下就可以了,对于普通的矩阵乘法是sigma( a(i, k) * b(k, j) ) (1<=k<=N), 现在的转移是min( a(i, k) + b(k, j)) (1<=k<=N)。这题预处理模拟的时候想想有点烦(不,是很烦),不过搞起来其实还是可以的,所以说所有题目都是纸老虎,首先不能被吓到!


code:

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
#include 
      
      
       
       
#include 
       
       
         #include 
        
          using namespace std; typedef __int64 ll; int tot, vis[11]; map 
         
           mp; string zt[55]; int changeto[55][55]; void dfs(int c, string str) { if(c == 6) { mp[str] = tot; zt[tot] = str; tot++; return ; } if(vis[c]) { dfs(c+1, str); return ; } int a[6]; vis[c] = 1; str += c + '0'; int tot = 0; for(int i = c+1;i <= 5; i++) if(!vis[i]) { a[tot++] = i; } int full = (1< 
          
            <= full; i++) { string nxt = str; for(int j = 0;j < tot; j++) if(i>>j&1) { vis[a[j]] = 1; nxt += a[j] + '0'; } nxt += ','; dfs(c+1, nxt); for(int j = 0;j < tot; j++) if(i>>j&1) vis[a[j]] = 0; } vis[c] = 0; } int find_fa(int fa[], int x) { return fa[x] = (fa[x] == x ? x : find_fa(fa, fa[x])); } void Union(int fa[], int x, int y) { x = find_fa(fa, x); y = find_fa(fa, y); if(x != y) fa[x] = y; } void init() { memset(vis, 0, sizeof(vis)); tot = 0; dfs(1, ""); for(int i = 0;i < tot; i++) { int fa[8]; for(int j = 1;j <= 6; j++) fa[j] = j; for(int j = 1;j < zt[i].size(); j++) if(zt[i][j]!=','&&zt[i][j-1]!=',') Union(fa, zt[i][j]-'0', zt[i][j-1]-'0'); for(int j = 1;j <= 6; j++) fa[j] = find_fa(fa, j); int full = (1<<5)-1; for(int j = 0;j <= full; j++) { int ff[8]; for(int k = 1;k <= 6; k++) ff[k] = fa[k]; for(int k = 0;k < 5; k++) if(j>>k&1) Union(ff, 6, 6-k-1); for(int k = 1;k <= 6; k++) ff[k] = find_fa(ff, k); bool flag = true; string nxt = ""; memset(vis, 0, sizeof(vis)); for(int k = 1;k <= 6; k++) if(!vis[k]){ int sum = 0; for(int l = k;l <= 6; l++) if(ff[k]==ff[l]) sum++; if(k == 1 && sum == 1) { flag = false; break; } if(k == 1) continue; for(int l = k;l <= 6; l++)if(ff[k]==ff[l]){ vis[l] = 1; if(l > 1) nxt += l-1+'0'; } nxt += ','; } if(!flag) changeto[i][j] = -1; else changeto[i][j] = mp[nxt]; } } } const ll INF = 1LL<<60; const int D = 52; struct matrix { ll a[D][D]; matrix() { memset(a, 0, sizeof(a)); } matrix operator * (const matrix&b)const { matrix ret; for(int i = 0;i < D; i++) { for(int j = 0;j < D; j++) { ret.a[i][j] = INF; for(int k = 0;k < D; k++) ret.a[i][j] = min(ret.a[i][j], a[i][k]+b.a[k][j]); } } return ret; } void print() { puts(""); for(int i = 0;i < D; i++) { for(int j = 0;j < D; j++) printf("%d ", a[i][j]); puts(""); } puts(""); } }; int val[11], n, seed; bool input() { return scanf("%d%d", &n, &seed) == 2; } matrix get(int d) { matrix ret; for(int i = 0;i < D; i++) for(int j = 0;j < D; j++) ret.a[i][j] = INF; for(int i = 0;i < D; i++) { int full = (1< 
           
             <= full; j++) if(changeto[i][j] != -1){ ll add = 0; for(int k = 0;k < d; k++) if(j>>k & 1) add += val[k]; ret.a[i][changeto[i][j]] = min(ret.a[i][changeto[i][j]], add); } } return ret; } matrix fuck[11]; ll solve() { if(n == 1) return 0; const int MOD = 2333333; matrix ans; for(int i = 0;i < D; i++) ans.a[0][i] = INF; ans.a[0][51] = 0; int x = seed; for(int i = 2;i <= 5; i++) { x = x*907%MOD; int T = x; for(int j = 1;j <= i-1; j++) { x = x*907%MOD; int w = T^x; val[i-j-1] = w; } matrix tmp = get(i-1); ans = ans*tmp; if(i == n) { return ans.a[0][51]; } } memset(fuck[0].a, 0, sizeof(fuck[0].a)); for(int i = 6;i <= 15; i++) { x = x*907%MOD; int T = x; for(int j = i-5;j <= i-1; j++) { x = x*907%MOD; int w = T^x; val[i-j-1] = w; } matrix tmp = get(5); if(i > 6) fuck[i-5] = fuck[i-5-1]*tmp; else fuck[i-5] = tmp; if(i == n) { ans = ans*fuck[i-5]; return ans.a[0][51]; } } int Time = (n-5)/9; while(Time) { if(Time&1) ans = ans*fuck[9]; fuck[9] = fuck[9]*fuck[9]; Time >>= 1; } if((n-5)%9 > 0) ans = ans*fuck[(n-5)%9]; return ans.a[0][51]; } int main() { init(); while(input()) { printf("%I64d\n", solve()); } return 0; } 
            
           
          
         
       
      
      
    
    
   
   

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值