第一次打 USACO,只打到了 Silver
给定一个长度为 n n n s s s s s s G 和 H 组成,求有多少个长度不小于 3 3 3 G 或 H。
3 ≤ n ≤ 5 × 1 0 5 3\le n \le 5\times 10^5 3 ≤ n ≤ 5 × 1 0 5 
考虑对于每一个位置 i i i i i i i i i s i s_i s i  p r e i pre_i p r e i  s i s_i s i  l s t i lst_i l s t i  
然后只需要分类讨论一下以 i i i G 还是 H。
s i = s i − 1 s_i=s_{i-1} s i  = s i − 1  s i − 1 ≠ s i − 2 s_{i-1}\neq s_{i-2} s i − 1    = s i − 2  s i s_i s i  s i s_i s i  s j s_j s j  s j s_j s j  s j s_j s j  否则,也就是只出现一次的字符是 s i s_i s i  s i s_i s i   
 
算是铜组最恶心的一道了吧,其实也不难就是细节比较多。
    Code 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include  <bits/stdc++.h>  #define  ll long long #define  db double #define  gc getchar #define  pc putchar using  namespace  std;namespace  IO{     template  <typename  T>     void  read (T &x)       {        x = 0 ; bool  f = 0 ; char  c = gc ();         while (!isdigit (c)) f |= c == '-' , c = gc ();         while (isdigit (c)) x = x * 10  + c - '0' , c = gc ();         if (f) x = -x;     }     template  <typename  T>     void  write (T x)       {        if (x < 0 ) pc ('-' ), x = -x;         if (x > 9 ) write (x / 10 );         pc ('0'  + x % 10 );     } } using  namespace  IO;const  int  N = 5e5  + 5 ;int  n, pre[N], lst[N];char  s[N];ll ans; int  main ()     read (n);     scanf ("%s" , s + 1 );     int  x = 0 , y = 0 ;     pre[n + 1 ] = n;     for (int  i = 1 ; i <= n; i++)     {         if (s[i] == 'G' )         {             pre[i] = x;             lst[i] = y ? y : n + 1 ;             x = i;         }         else          {             pre[i] = y;             lst[i] = x ? x : n + 1 ;              y = i;         }     }     for (int  i = 3 ; i <= n; i++)     {         if (s[i - 1 ] == s[i] || s[i - 2 ] != s[i - 1 ]) ans += max (min (lst[i], i - 2 ) - pre[lst[i]], 0 );         else  ans += max (i - pre[i] - 2 , 0 );     }     write (ans), pc ('\n' );     return  0 ; } 
 
给定两个长为 n n n a , b a,b a , b a a a + 1 +1 + 1 − 1 -1 − 1 
1 ≤ n ≤ 1 0 5 1\le n \le 10^5 1 ≤ n ≤ 1 0 5 
直接做差,然后就转化成典中典了
就是有正有负,通过手玩不难发现每次操作不可能同时操作正负,分成两边单独操作一样是最优的
所以只需要指针扫一遍找出每个正、负区间,然后跑一遍单调栈即可。
    Code 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include  <bits/stdc++.h>  #define  ll long long #define  db double #define  gc getchar #define  pc putchar using  namespace  std;namespace  IO{     template  <typename  T>     void  read (T &x)       {        x = 0 ; bool  f = 0 ; char  c = gc ();         while (!isdigit (c)) f |= c == '-' , c = gc ();         while (isdigit (c)) x = x * 10  + c - '0' , c = gc ();         if (f) x = -x;     }     template  <typename  T>     void  write (T x)       {        if (x < 0 ) pc ('-' ), x = -x;         if (x > 9 ) write (x / 10 );         pc ('0'  + x % 10 );     } } using  namespace  IO;const  int  N = 1e6  + 5 ;int  n, a[N];int  stk[N], top;ll ans; int  main ()     read (n);     for (int  i = 1 ; i <= n; i++) read (a[i]);     for (int  i = 1 , b; i <= n; i++) read (b), a[i] = b - a[i];     for (int  l = 1 , r; l <= n; l = r + 1 )     {         r = l + 1 ;         while (r <= n && a[r] * a[r - 1 ] > 0 ) r++;         r--;         top = 0 ;         for (int  i = l; i <= r; i++)         {             a[i] = abs (a[i]);             if (a[i] > stk[top]) ans += a[i] - stk[top];             while (top && stk[top] > a[i]) top--;             stk[++top] = a[i];         }     }     write (ans), pc ('\n' );     return  0 ; } 
 
给定一个 n × n n\times n n × n . 可以通过,H 不可以通过,只能向右或向下走,求在至多转向 k k k 
2 ≤ n ≤ 50 , 1 ≤ k ≤ 3 2\le n \le 50,1\le k\le 3 2 ≤ n ≤ 5 0 , 1 ≤ k ≤ 3 
很容易想到状态
f i , j , k , d f_{i,j,k,d} f i , j , k , d  ( 1 , 1 ) (1,1) ( 1 , 1 ) ( i , j ) (i,j) ( i , j ) k k k d ( 0 右 , 1 下 ) d(0右,1下) d ( 0 右 , 1 下 ) 
转移也比较简单,对于 d = 0 / 1 d=0/1 d = 0 / 1 
    Code 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 #include  <bits/stdc++.h>  #define  ll long long #define  db double #define  gc getchar #define  pc putchar using  namespace  std;namespace  IO{     template  <typename  T>     void  read (T &x)       {        x = 0 ; bool  f = 0 ; char  c = gc ();         while (!isdigit (c)) f |= c == '-' , c = gc ();         while (isdigit (c)) x = x * 10  + c - '0' , c = gc ();         if (f) x = -x;     }     template  <typename  T>     void  write (T x)       {        if (x < 0 ) pc ('-' ), x = -x;         if (x > 9 ) write (x / 10 );         pc ('0'  + x % 10 );     } } using  namespace  IO;const  int  N = 55 ;int  n, m;char  s[N][N];int  f[N][N][10 ][2 ];void  solve ()     memset (f, 0 , sizeof      read (n), read (m);     for (int  i = 1 ; i <= n; i++)         scanf ("%s" , s[i] + 1 );     f[1 ][0 ][0 ][0 ] = f[0 ][1 ][0 ][1 ] = 1 ;     for (int  i = 1 ; i <= n; i++)     {         if (s[1 ][i] == '.' ) f[1 ][i][0 ][0 ] = f[1 ][i - 1 ][0 ][0 ];         if (s[i][1 ] == '.' ) f[i][1 ][0 ][1 ] = f[i - 1 ][1 ][0 ][1 ];     }     for (int  i = 2 ; i <= n; i++)         for (int  j = 2 ; j <= n; j++)         {             if (s[i][j] == 'H' ) continue ;             for (int  k = 0 ; k <= m; k++)             {                 f[i][j][k][0 ] += f[i][j - 1 ][k][0 ];                 if (k) f[i][j][k][0 ] += f[i][j - 1 ][k - 1 ][1 ];                 f[i][j][k][1 ] += f[i - 1 ][j][k][1 ];                 if (k) f[i][j][k][1 ] += f[i - 1 ][j][k - 1 ][0 ];             }         }     ll ans = 0 ;     for (int  i = 0 ; i <= m; i++)         ans += f[n][n][i][0 ] + f[n][n][i][1 ];     write (ans), pc ('\n' ); } int  main ()     int  T; read (T);     while (T--) solve ();     return  0 ; } 
 
在一条数轴上,有 k k k p i p_i p i  t i t_i t i  m m m f 1 , f 2 , … f m f_1,f_2,\dots f_m f 1  , f 2  , … f m  n n n 
对于每个点,当这个点距离你占的最近位置小于对方占的最近位置时,你可以获得这个点的权值。
1 ≤ k ≤ 2 × 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 1\le k \le 2\times 10^5,1\le m\le 2\times 10^5 1 ≤ k ≤ 2 × 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 k + m k+m k + m [ 0 , 1 0 9 ] [0,10^9] [ 0 , 1 0 9 ] 
考虑对方占的每两个位置之间
当在这个区间内放 2 2 2 
所以只需要计算只放 1 1 1 
因为在这个区间内的点距离对面占的最近位置就是到两端的最小值,所以我们可以知道要想得到任意一个点的权值需要在哪个区间占位置
画一下图发现从左到右的点需要的区间的左右端点都是递增的,所以就可以双指针乱扫了(
我们有了在每个区间内占 1 / 2 1/2 1 / 2 
这里需要反悔贪心,先把占一个的权值都加到大根堆里,然后在被选时,将占两个的权值减去占一个的权值 加到大根堆里,这样在下次选到时相当于占了两个。
T T T n n n m m m 1 1 1 n n n 
建一条从 i i i j j j ( i − j ) 2 (i-j)^2 ( i − j ) 2 
1 ≤ T ≤ 20 , 1 ≤ n ≤ 1 0 5 , ∑ ( n + m ) ≤ 5 × 1 0 5 1\le T \le 20,1\le n \le 10^5,\sum(n+m)\le 5\times 10^5 1 ≤ T ≤ 2 0 , 1 ≤ n ≤ 1 0 5 , ∑ ( n + m ) ≤ 5 × 1 0 5 
先用并查集求出每个连通块,然后只需要求出 1 1 1 n n n 
在求最短距离时可以直接枚举每个点,在 1 1 1 n n n 
但我考场上没看见最后一个数据范围,就以为 O ( T n log  n ) O(Tn\log n) O ( T n log  n ) 
    Code 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include  <bits/stdc++.h>  #define  ll long long #define  db double #define  gc getchar #define  pc putchar #define  pb push_back using  namespace  std;namespace  IO{     template  <typename  T>     void  read (T &x)       {        x = 0 ; bool  f = 0 ; char  c = gc ();         while (!isdigit (c)) f |= c == '-' , c = gc ();         while (isdigit (c)) x = x * 10  + c - '0' , c = gc ();         if (f) x = -x;     }     template  <typename  T>     void  write (T x)       {        if (x < 0 ) pc ('-' ), x = -x;         if (x > 9 ) write (x / 10 );         pc ('0'  + x % 10 );     } } using  namespace  IO;const  int  N = 1e5  + 5 ;int  n, m, f[N], id[N], cnt;vector <int > vec[N]; int  Find (int  a)     return  f[a] == a ? a : f[a] = Find (f[a]); } int  d1[N], d2[N];void  work ()     read (n), read (m);     for (int  i = 1 ; i <= n; i++) f[i] = i;     for (int  i = 1 ; i <= m; i++)     {         int  u, v; read (u), read (v);         if (Find (u) != Find (v)) f[Find (v)] = Find (u);     }     if (Find (1 ) == Find (n))     {         puts ("0" );         return ;     }     cnt = 0 ;     for (int  i = 1 ; i <= n; i++)     {         int  x = Find (i);         if (!id[x]) id[x] = ++cnt;         vec[id[x]].pb (i);     }     int  S = id[Find (1 )], T = id[Find (n)];     memset (d1, 0x3f , sizeof      memset (d2, 0x3f , sizeof      for (int  i = 1 ; i <= n; i++)     {         int  x = id[Find (i)];         int  pos = lower_bound (vec[S].begin (), vec[S].end (), i) - vec[S].begin ();         d1[x] = min (d1[x], min (pos ? abs (i - vec[S][pos - 1 ]) : n, pos < (int )vec[S].size () ? abs (i - vec[S][pos]) : n));         pos = lower_bound (vec[T].begin (), vec[T].end (), i) - vec[T].begin ();         d2[x] = min (d2[x], min (pos ? abs (i - vec[T][pos - 1 ]) : n, pos < (int )vec[T].size () ? abs (i - vec[T][pos]) : n));     }     ll ans = 1ll  * (n - 1 ) * (n - 1 );     for (int  i = 1 ; i <= cnt; i++)         ans = min (ans, 1ll  * d1[i] * d1[i] + 1ll  * d2[i] * d2[i]);     write (ans), pc ('\n' );     memset (id, 0 , sizeof      for (int  i = 1 ; i <= cnt; i++) vec[i].clear ();     return ; } signed  main ()     int  T; read (T);     while (T--) work ();     return  0 ; } 
 
给定 n n n i i i [ a i , b i ] [a_i,b_i] [ a i  , b i  ] 0 ≤ k ≤ 2 m 0\le k \le 2m 0 ≤ k ≤ 2 m k k k a i + a j ≤ k ≤ b i + b j a_i+a_j\le k \le b_i+b_j a i  + a j  ≤ k ≤ b i  + b j  
1 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 5000 , a i ≤ b i 1\le n\le 2\times 10^5,1\le m\le 5000,a_i\le b_i 1 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 5 0 0 0 , a i  ≤ b i  
只要你发现 n ≤ 2 × 1 0 5 n\le 2\times10^5 n ≤ 2 × 1 0 5 m ≤ 5000 m\le 5000 m ≤ 5 0 0 0 
因此我们可以开个桶,然后 O ( m 2 ) O(m^2) O ( m 2 ) a i + a j = k a_i+a_j=k a i  + a j  = k b i + b j = k b_i+b_j=k b i  + b j  = k a i = a j a_i=a_j a i  = a j  
考虑 k k k k + 1 k+1 k + 1 a i + a j = k + 1 a_i+a_j=k+1 a i  + a j  = k + 1 b i + b j = k b_i+b_j=k b i  + b j  = k 
也相当于差分,可以在最后求个前缀和。
    Code 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include  <bits/stdc++.h>  #define  ll long long #define  db double #define  gc getchar #define  pc putchar using  namespace  std;namespace  IO{     template  <typename  T>     void  read (T &x)       {        x = 0 ; bool  f = 0 ; char  c = gc ();         while (!isdigit (c)) f |= c == '-' , c = gc ();         while (isdigit (c)) x = x * 10  + c - '0' , c = gc ();         if (f) x = -x;     }     template  <typename  T>     void  write (T x)       {        if (x < 0 ) pc ('-' ), x = -x;         if (x > 9 ) write (x / 10 );         pc ('0'  + x % 10 );     } } using  namespace  IO;const  int  N = 2e5  + 5 ;int  n, m, a[N], b[N];ll ca[N], cb[N]; ll l[N], r[N]; int  main ()     read (n), read (m);     for (int  i = 1 ; i <= n; i++) read (a[i]), read (b[i]), ca[a[i]]++, cb[b[i]]++;     for (int  i = 0 ; i <= m; i++)         for (int  j = 0 ; j <= m; j++)             if (i != j) l[i + j] += ca[i] * ca[j], r[i + j] += cb[i] * cb[j];     for (int  i = 0 ; i <= m; i++) l[i + i] += ca[i] * ca[i], r[i + i] += cb[i] * cb[i];     ll ans = 0 ;     for (int  i = 0 ; i <= m + m; i++)         ans += l[i], write (ans), pc ('\n' ), ans -= r[i];     return  0 ; }