ZOJ Monthly, June 2014 解题报告

A.Another Recurrence Sequence

B.Gears

题目大意:有n个齿轮,一开始各自为一组,之后进行m次操作,包括以下4种类型:
1.合并两组齿轮,合并的两个应该反向旋转
2.把某个齿轮从所在组删除,自为一组,但不影响同组其它齿轮的状态与关系
3.询问两个齿轮是同向、反向或无关系(即不在同一组)
4.询问某个齿轮所在组的齿轮总数

分析:典型的并查集操作,但是注意两点:
1.由于操作3要询问两个齿轮的相对状态,因此对并查集中每个元素应当保存它的状态信息。状态是相对的,只需要保存每个元素相对于父元素的状态,在查询所属集合时顺便更新状态信息。如用depth[i]表示齿轮i相对于父齿轮的深度,那么两个齿轮属于同一组且深度奇偶性相同时同向旋转。更新depth数组也行简单,i对j的深度 = i对k的深度 + k对j的深度。
2.操作2涉及删除某个元素。另外开一个数组id,用id[i]表示元素i实际所在的位置,当删除元素i时,令id[i] = new id,同时fa[id[i]] = id[i],即id[i]自为一组。这样一来,之后涉及到元素i的操作都用id[i]代替,而原来的i元素依然存在,只不过它只起到占位的作用,以维持原集合的相对关系。相当于id[i]表示元素i的指针。

#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
          #include 
          #include 
          
            #include 
           
             #include 
            
              #include 
             
               #include 
              
                #include 
               
                 #include 
                
                  #include 
                 
                   #include 
                  
                    #include 
                   
                     #include 
                    
                      #define mp make_pair #define X first #define Y second #define MEMSET(a, b) memset(a, b, sizeof(a)) using namespace std; typedef unsigned int ui; typedef long long ll; typedef unsigned long long ull; typedef pair 
                     
                       pii; typedef vector 
                      
                        vi; typedef vi::iterator vi_it; typedef map 
                       
                         mii; typedef priority_queue 
                        
                          pqi; typedef priority_queue 
                         
                           , greater 
                          
                            > rpqi; typedef priority_queue 
                           
                             pqp; typedef priority_queue 
                            
                              , greater 
                             
                               > rpqp; const int MAX_N = 200000 + 2; int fa[MAX_N * 3], num[MAX_N * 3], depth[MAX_N * 3]; int id[MAX_N * 3]; int cnt; int find_fa(int x) { if (fa[x] == x) { return x; } int tmp = fa[x]; fa[x] = find_fa(fa[x]); depth[x] += depth[tmp]; return fa[x]; } void union_set(int x, int y) { int headx = find_fa(x); int heady = find_fa(y); if (headx != heady) { fa[heady] = headx; depth[heady] = depth[y] + 1 + depth[x]; num[headx] += num[heady]; } } void del(int x) { --num[find_fa(id[x])]; id[x] = ++cnt; x = id[x]; fa[x] = x; num[x] = 1; depth[x] = 0; } int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); int n, m, i; while (scanf("%d%d", &n, &m) == 2) { MEMSET(depth, 0); cnt = n; for (i = 1; i <= n; ++i) { id[i] = i; fa[i] = i; num[i] = 1; } while (m--) { char ch; scanf("\n%c", &ch); if (ch == 'L') { int a, b; scanf("%d%d", &a, &b); a = id[a], b = id[b]; union_set(a, b); } else if (ch == 'D') { int a; scanf("%d%d", &a); del(a); } else if (ch == 'Q') { int a, b; scanf("%d%d", &a, &b); a = id[a], b = id[b]; if (find_fa(a) != find_fa(b)) { puts("Unknown"); } else if ((depth[a] & 1) == (depth[b] & 1)) { puts("Same"); } else { puts("Different"); } } else { int a; scanf("%d", &a); a = id[a]; printf("%d\n", num[find_fa(a)]); } } } return 0; } 
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
        
       
       
      
      
     
     
    
    

C.Consecutive Blocks

题目大意:给定一个整数序列,要求删除最多k个数后,新序列中连续相等的子序列长度最大,输出该最大长度。

分析:除了F题搞笑外这个就是最简单的题了。记录每个数出现的位置(由于数字可能很大,先把数字映射到1..k),然后计算以每个位置结束时的最大长度即可。

D.An Easy Game

题目大意:给定两个0-1序列s1, s2,操作t次,每次改变m个位置,求把s1改变为s2的方法总数。

分析:明显的DP,注意s1和s2哪些位置相同并不重要,重要的是有几个位置不同。改变的时候也一样,改变哪些位置并不重要,重要的是改变之后有几个位置不同。所以用dp[i][j]表示i次操作之后有j个位置不同的方法数,答案就是dp[t][0]。对于dp[i - 1][j],经过一次操作之后假设把k个位置从不同变为相同,剩下m - k个位置从相同变为不同,那么
dp[i][j + m - k - k] += dp[i - 1][j] * C(j, k) * C(n - j, m - k)
其中C(j, k)表示组合数,即从j个不同的位置选出k个改变。循环k即可。

#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
          #include 
          #include 
          
            #include 
           
             #include 
            
              #include 
             
               #include 
              
                #include 
               
                 #include 
                
                  #include 
                 
                   #include 
                  
                    #include 
                   
                     #define mp make_pair #define X first #define Y second #define MEMSET(a, b) memset(a, b, sizeof(a)) using namespace std; typedef unsigned int ui; typedef long long ll; typedef unsigned long long ull; typedef pair 
                    
                      pii; typedef vector 
                     
                       vi; typedef vi::iterator vi_it; typedef map 
                      
                        mii; typedef priority_queue 
                       
                         pqi; typedef priority_queue 
                        
                          , greater 
                         
                           > rpqi; typedef priority_queue 
                          
                            pqp; typedef priority_queue 
                           
                             , greater 
                            
                              > rpqp; const int MAX_N = 100 + 2; const int MOD = 1000000009; char str[2][MAX_N]; ll dp[MAX_N][MAX_N]; int C[MAX_N][MAX_N]; void compute_C() { C[0][0] = 1; for (int i = 1; i <= 100; ++i) { C[i][0] = 1; for (int j = 1; j <= i; ++j) { C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD; } } } int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); compute_C(); int n, t, m, i, j, k; while (scanf("%d%d%d\n", &n, &t, &m) == 3) { gets(str[0]); gets(str[1]); int cnt = 0; for (i = 0; i < n; ++i) { cnt += str[0][i] != str[1][i]; } MEMSET(dp, 0); dp[0][cnt] = 1; for (i = 1; i <= t; ++i) { for (j = 0; j <= n; ++j) { for (k = max(0, m - n + j); k <= j && k <= m; ++k) { dp[i][j + m - k - k] = (dp[i][j + m - k - k] + dp[i - 1][j] * C[j][k] % MOD * C[n - j][m - k] % MOD) % MOD; } } } printf("%d\n", dp[t][0]); } return 0; } 
                             
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
        
       
       
      
      
     
     
    
    


题目大意:给定一个无向图,要求删除一些边使点p和点q不连通,首先是使花费最少,若有多组解则使删除的边最少。

分析:典型的最小割,最大流即可。只需要把每边的花费c改为c = c * (|E| + 1) + 1即可使删除的边最少,其中|E|为边数。为方便把|E|换为一个较大的数也可以。

#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
         
         
#include 
         
         
           #include 
           #include 
           
             #include 
            
              #include 
             
               #include 
              
                #include 
               
                 #include 
                
                  #include 
                 
                   #include 
                  
                    #include 
                   
                     #include 
                    
                      #define mp make_pair #define X first #define Y second #define MEMSET(a, b) memset(a, b, sizeof(a)) using namespace std; typedef unsigned int ui; typedef long long ll; typedef unsigned long long ull; typedef pair 
                     
                       pii; typedef vector 
                      
                        vi; typedef vi::iterator vi_it; typedef map 
                       
                         mii; typedef priority_queue 
                        
                          pqi; typedef priority_queue 
                         
                           , greater 
                          
                            > rpqi; typedef priority_queue 
                           
                             pqp; typedef priority_queue 
                            
                              , greater 
                             
                               > rpqp; const int MAX_N = 50 + 2; const int INF = (int)1.0e9; int g[MAX_N][MAX_N]; int dis[MAX_N]; int q[MAX_N]; bool bfs(int source, int sink, int n) { MEMSET(dis, -1); dis[source] = 0; int head = 0, tail = 0; q[tail++] = source; while (head != tail) { int k = q[head++]; for (int i = 1; i <= n; ++i) { if (g[k][i] > 0 && dis[i] < 0) { dis[i] = dis[k] + 1; q[tail++] = i; } } } return dis[sink] > 0; } int dfs(int n, int sink, int x, int mx) { if (x == sink) { return mx; } int a; for (int i = 1; i <= n; ++i) { if (g[x][i] > 0 && dis[i] == dis[x] + 1 && (a = dfs(n, sink, i, min(mx, g[x][i])))) { g[x][i] -= a; g[i][x] += a; return a; } } return 0; } int dinic(int n, int source, int sink) { int ret = 0; while (bfs(source, sink, n)) { ret += dfs(n, sink, source, INF); } return ret; } int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); int T; cin >> T; while (T--) { int n, m, p, q, sum = 0; scanf("%d%d%d%d", &n, &m, &p, &q); MEMSET(g, 0); while (m--) { int a, b, c; scanf("%d%d%d", &a, &b, &c); sum += c; g[a][b] += c * 1111 + 1; g[b][a] += c * 1111 + 1; } int ans = dinic(n, p, q); if (!ans) { puts("Inf"); } else { printf("%.2lf\n", (double)(sum - ans / 1111) / (ans % 1111)); } } return 0; } 
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
         
        
        
       
       
      
      
     
     


分析:搞笑题,就不解释了。



题目大意:给定一个有向图,要求把点分为k个集合,使得每个集合中的任意两点a, b满足a, b互相不可到达。

分析:求出强连通分量后缩点,得到有向无环图,dfs该图求出各点深度(深度加权,权值为强连通分量大小),深度最大值即答案,因为这一条路径上任意两点都可从深度小的一点到达深度大的一点,所以必定属于不同集合;又可以把其它路径(长度为len)上的各点依次归到集合1..len。

#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
         
         
#include 
         
         
           #include 
           #include 
           
             #include 
            
              #include 
             
               #include 
              
                #include 
               
                 #include 
                
                  #include 
                 
                   #include 
                  
                    #include 
                   
                     #include 
                    
                      #define mp make_pair #define X first #define Y second #define MEMSET(a, b) memset(a, b, sizeof(a)) using namespace std; typedef unsigned int ui; typedef long long ll; typedef unsigned long long ull; typedef pair 
                     
                       pii; typedef vector 
                      
                        vi; typedef vi::iterator vi_it; typedef map 
                       
                         mii; typedef priority_queue 
                        
                          pqi; typedef priority_queue 
                         
                           , greater 
                          
                            > rpqi; typedef priority_queue 
                           
                             pqp; typedef priority_queue 
                            
                              , greater 
                             
                               > rpqp; const int MAX_N = 100000 + 2; vi link[MAX_N], relink[MAX_N]; int dfn[MAX_N], low[MAX_N], belong[MAX_N], num[MAX_N]; int stk[MAX_N]; int top, idx, cnt; int len[MAX_N]; void dfs(int u) { dfn[u] = low[u] = ++idx; stk[top++] = u; int v; for (int i = 0; i < link[u].size(); ++i) { if (!dfn[v = link[u][i]]) { dfs(v); low[u] = min(low[u], low[v]); } else if (!belong[v]) { low[u] = min(low[u], dfn[v]); } } if (dfn[u] == low[u]) { num[belong[u] = ++cnt] = 1; while (stk[--top] != u) { ++num[belong[stk[top]] = cnt]; } } } void tarjan(int n) { MEMSET(belong, 0); MEMSET(dfn, 0); cnt = idx = top = 0; for (int i = 1; i <= n; ++i) { if (!dfn[i]) { dfs(i); } } } void rebuild_graph(int n) { int i, j; for (i = 1; i <= cnt; ++i) { relink[i].clear(); } for (i = 1; i <= n; ++i) { for (j = 0; j < link[i].size(); ++j) { if (belong[i] != belong[link[i][j]]) { relink[belong[i]].push_back(belong[link[i][j]]); } } } } int _dfs(int u) { if (len[u]) { return len[u]; } for (int i = 0; i < relink[u].size(); ++i) { len[u] = max(len[u], _dfs(relink[u][i])); } return len[u] += num[u]; } int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); int n, m, i; while (scanf("%d%d", &n, &m) == 2) { for (i = 1; i <= n; ++i) { link[i].clear(); } while (m--) { int s, t; scanf("%d%d", &s, &t); link[s].push_back(t); } tarjan(n); rebuild_graph(n); MEMSET(len, 0); int ans = 0; for (i = 1; i <= cnt; ++i) { ans = max(ans, _dfs(i)); } printf("%d\n", ans); } return 0; } 
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
         
        
        
       
       
      
      
     
     



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值