Codeforces Round #250 (Div.1) 解题报告

一般比赛都是晚上11:30,这次换了时间,所以……就忘了……


A. The Child and Toy


分析:水题。


B. The Child and Zoo


分析:n最大为10^5,当然不可能真的逐对点考虑。反过来想一想每一条边:什么时候那个小孩会走这条边?特别地,路径最小值在这条边上取到(两个端点中的最小值,可以先做预处理,把点权转换为边权)。使用并查集,一开始每个点自成一集合,我们把边按权值从大到小排序,每一次处理完该边后合并该边端点所在的集合,那么每个集合中的任意点对都是连通的。考虑在处理新的一条边edge(端点为u, v,权值为w)时,假设它的两个端点u, v所在的两个集合分别为S1, S2并且S1 != S2。对于任意点a∈S1,点b∈S2,a → u(表示a, u连通,因为a, u属于同一集合), u → v, v → b,故a → b,并且这条路径上的最小值就是w(因为按边权从大到小处理每一条边),所以f(a, b) ≥ w。另一方面,假设f(a, b) = x > w,即是说存在另一条从a到b的路径,这条路径上的每一条边权值都大于w,那么这条路径上的每一条边都会在edge之前被处理,所以集合S1, S2也一定在之前被合并,矛盾。故f(a, b) = w。这意味着,总共至少有2 * |S1| * |S2|个点对(a, b)满足f(a, b) = w,记这个点对集合为SS(edge)。那么会不会有别的路径漏掉了呢?因为每条路径必有一条最小边,而我们又依次考察了每一条边,故U(SS(edge)) = 全集,其中U表示集合的并。另一方面,由于对不同集合中的点对(a1, b1)和(a2, b2),f(a1, b1) != f(a2, b2),故(a1, b1) != (a2, b2),即SS(edge)两两不相交,于是SS(edge)就是所有点对的一个划分。这就说明了有且仅有2 * |S1| * |S2|个点对(a, b)满足f(a, b) = w。

#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 = 100000 + 2; int a[MAX + 2]; int sz[MAX + 2], fa[MAX + 2]; struct edge { int u; int v; int cost; bool operator < (const edge &eg) const { return cost > eg.cost; } } eg[MAX + 2]; int find_fa(int x) { return fa[x] == x ? x : fa[x] = find_fa(fa[x]); } int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); int n, m, i; cin >> n >> m; for (i = 1; i <= n; ++i) { scanf("%d", a + i); fa[i] = i; sz[i] = 1; } for (i = 0; i < m; ++i) { scanf("%d%d", &eg[i].u, &eg[i].v); eg[i].cost = min(a[eg[i].u], a[eg[i].v]); } sort(eg, eg + m); ll ans = 0; for (i = 0; i < m; ++i) { int x = find_fa(eg[i].u); int y = find_fa(eg[i].v); if (x != y) { ans += (ll)sz[x] * sz[y] * eg[i].cost; fa[x] = y; sz[y] += sz[x]; } } printf("%lf\n", 2.0 * ans / n / (n - 1)); return 0; } 
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
       
      
      
     
     
    
    
   
   

C. The Child and Polygon


分析:用dp[i][j]表示多边形i, i + 1, ..., j, i的划分方法数,那么枚举从顶点i连边的顶点k(可能顶点i不连边),然后递推关系为dp[i][j] += dp[i][k] * dp[k][j]。

#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 = 200 + 2; const int MOD = 1000000007; int x[MAX_N], y[MAX_N]; ll dp[MAX_N][MAX_N]; bool inc(int x1, int y1, int x2, int y2) { return (ll)x1 * y2 - (ll)x2 * y1 > 0; } int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); int n; ll s = 0; cin >> n; for (int i = 0; i < n; ++i) { cin >> x[i] >> y[i]; } for (int i = 0; i < n; ++i) { s += (ll)x[i] * y[(i + 1) % n] - (ll)x[(i + 1) % n] * y[i]; } if (s < 0) { reverse(x, x + n); reverse(y, y + n); } for (int i = 0; i + 1 < n; ++i) { dp[i][i + 1] = 1; } for (int len = 2; len < n; ++len) { for (int i = 0; i + len < n; ++i) { int j = i + len; for (int k = i + 1; k < j; ++k) { if (inc(x[k] - x[i], y[k] - y[i], x[j] - x[i], y[j] - y[i])) { dp[i][j] = (dp[i][j] + dp[i][k] * dp[k][j]) % MOD; } } } } printf("%I64d\n", dp[0][n - 1]); return 0; } 
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
       
      
      
     
     
    
    
   
   

D. The Child and Sequence


分析:单点更新,成段更新,成段求和,第一想法就是线段树。问题是成段更新不是加减运算,而是取模运算,直接做标记不方便处理,于是放弃标记,直接把取模运算处理到叶子结点,最坏情况下要更新[l, r]中每一个数。但是是不是一定要处理到叶子结点呢?不一定的,因为如果a < x,那么a % x = x。也就是说只有当模x不超过a时取模运算才有意义,于是记录每个区间的最大值,如果x大于这个最大值就不必更新这个区间了。如果每次模x都取得很合适,那么最坏情况下是不是还是要更新到每一个叶子结点呢?先抛开单点更新不管,假设m次操作全是成段更新(即取模运算),我们可以分析一下每一个数最多被更新多少次。

设一开始的数列为a[i],经过一次更新后,a[i] = b[i],这意味着b[i] = a[i] % x ≤ a[i] - x,并且b[i] = a[i] % x < x,于是b[i] ≤ min(a[i] - x, x) ≤ a[i] / 2。现在就很清晰了,在只有成段更新的情况下,每个数最多更新log(a[i])次,总时间复杂度为O(nlog(MAX_VALUE))。加上单点更新的情况,最坏情况下每次单点更新都会导致这个数最多被多更新log(MAX_VALUE)次,所以总的不会超过O(mlog(MAX_VALUE)),那么在m, n同阶的情况下,最终的时间复杂度就是O(nlog(MAX_VALUE))。

#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 LL(x) ((x) << 1) #define RR(x) ((x) << 1 | 1) #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; int cnt; struct { int left; int right; ll sum; int maxv; inline int mid() { return (left + right) >> 1; } } st[MAX_N * 3]; inline void update(int idx) { int lc = LL(idx), rc = RR(idx); st[idx].sum = st[lc].sum + st[rc].sum; st[idx].maxv = max(st[lc].maxv, st[rc].maxv); } void build(int l, int r, int idx) { st[idx].left = l; st[idx].right = r; if (l != r) { int mid = st[idx].mid(); build(l, mid, LL(idx)); build(mid + 1, r, RR(idx)); update(idx); } else { scanf("%d", &st[idx].sum); st[idx].maxv = st[idx].sum; } } void update(int pos, int v, int idx) { if (st[idx].left == st[idx].right) { st[idx].maxv = st[idx].sum = v; return; } int mid = st[idx].mid(); if (pos <= mid) update(pos, v, LL(idx)); else update(pos, v, RR(idx)); update(idx); } void update(int l, int r, int mod, int idx) { if (st[idx].maxv < mod) { return; } if (st[idx].left == st[idx].right) { st[idx].maxv = st[idx].sum = st[idx].sum % mod; return; } int mid = st[idx].mid(); if (l <= mid) update(l, r, mod, LL(idx)); if (r > mid) update(l, r, mod, RR(idx)); update(idx); } ll query(int l, int r, int idx) { if (st[idx].left == l && st[idx].right == r) { return st[idx].sum; } int mid = st[idx].mid(); if (r <= mid) return query(l, r, LL(idx)); else if (l > mid) return query(l, r, RR(idx)); else return query(l, mid, LL(idx)) + query(mid + 1, r, RR(idx)); } int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); int n, m, i; cin >> n >> m; cnt = 1; build(1, n, 1); while (m--) { int type; scanf("%d", &type); if (type == 1) { int a, b; scanf("%d%d", &a, &b); printf("%I64d\n", query(a, b, 1)); } else if (type == 2) { int a, b, x; scanf("%d%d%d", &a, &b, &x); update(a, b, x, 1); } else { int k, x; scanf("%d%d", &k, &x); update(k, x, 1); } } return 0; } 
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
       
      
      
     
     
    
    
   
   

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值