Loading...

8.4集训

上午

考试

第一题

给你一个初始序列,和两个操作:操作1可以使得\(i\)\(i+2\)两个数交换位置,操作2是相邻的数可以交换位置

问:这个序列最少用多少次操作2可是使序列单调递增?

你考虑,操作1的性质:使得\(i\)\(i+2\)两个数交换位置,不就是奇偶性相同的位置吗?而操作2的性质:相邻的数可以交换位置,不就是奇偶性不同的数吗?

把原序列排序之后,对于为排序的序列,我们考虑当前这个数的下标与排完序之后的下标,如果发现他们奇偶性相同,也就是说,可以经过若干次操作1可以变换到那个位置

如果发现奇偶性不同,说明肯定是经过了一次操作1,因此计数器叠加

注意细节:这样统计是真实答案的二倍,因为:假如当前在\(i\),你发现这个数排完序之后在\(j\)\(i < j\)),且\(i,j\)的奇偶性不同,这时你计数器累加,但是到了\(j\)那个位置,你的计数器还会叠加,也就是说,你的计数器对于同一种情况增加了两次

#include <bits/stdc++.h>
#define LL long long
#define debug
using namespace std;

const int N = 2e5+66;

int n, res;
int a[N], b[N];

inline int thestars() {
   cin >> n;
   for (int i = 1; i <= n; ++ i) {
      cin >> a[i];
      b[i] = a[i];
   }
   sort(b+1, b+n+1);
   for (int i = 1; i <= n; ++ i) {
      int pos = lower_bound(b+1, b+n+1, a[i]) - b;
      if ((pos&1) != (i&1)) ++ res;
   }
   cout << (res/2);
   return 0;
}

int youngore = thestars();

signed main() {;}

第二题

\(10^8\)条横向道路与竖向道理,相邻的道路之间相差\(100m\),从左到右第\(i\)条纵向街道和从下到上第\(j\)条横向街道之间的交点描述为\((i,j)\),在某些交点上建有以交点为圆心,半径是 \(10\) 米的喷泉,行走时不能穿过喷泉。

问你要从\((x_1,x_2)\)走到\((x_2,y_2)\)最短要走多少?

Sample Input
1 1 6 5
3
3 2
5 3
2 4
Sample Output
891.415926535897938

\(10^8\)条横向道路与竖向道理,相邻的道路之间相差\(100m\),从左到右第\(i\)条纵向街道和从下到上第\(j\)条横向街道之间的交点描述为\((i,j)\),在某些交点上建有以交点为圆心,半径是 \(10\) 米的喷泉,行走时不能穿过喷泉。

问你要从\((X_1,Y_2)\)走到\((X_2,Y_2)\)最短要走多少?

由于所有的喷泉横纵坐标都相等,所以显然走尽可能多的喷泉可以是路径更短,

对于一个喷泉可以短\(20-\dfrac 1 4* 2\pi r\)

至此问题完成转化:最长上升子序列问题

不妨设\(X_1<X_2,Y_1<Y_2\),仅当\(X_1<x_i<X_2,Y_1<y_i<Y_2\)时候,统计这个点对答案的影响

并把所有点按照横坐标排序,求\(y_i\)\(Y_1\)为开始,\(Y_2\)终止的最长上升子序列

对于正常的\(dp\)做法,过不去的,需要用\(nlog_n\)的做法,我想,下文会提到的吧

#include <bits/stdc++.h>
#define LL long long
#define debug
using namespace std;

const int N = 3e5+66;

int n, ans, f[N];
int sx, sy, tx, ty;

struct node{int x, y;}a[N];

inline int cmp(node s, node t){
   if (s.x == t.x) return s.y > t.y;
   if (sy < ty) return s.x < t.x;
   return s.x > t.x;
}

inline int thestars() {
   cin >> sx >> sy >> tx >> ty >> n;
   if (tx < sx) swap(sx, tx), swap(sy, ty);
   for (int i = 1; i <= n; ++ i) cin >> a[i].x >> a[i].y;
   sort(a+1, a+n+1, cmp);
   double mhd = tx - sx + abs(ty - sy);
   f[0] = -0x7fffffff;
   for (int i = 1; i <= n; ++ i) {
      if (a[i].x > max(sx, tx) || a[i].x < min(sx, tx) || a[i].y > max(sy, ty) || a[i].y < min(sy, ty)) continue;
      if (a[i].y > f[ans]) f[++ ans] = a[i].y;
      else if (a[i].y < f[ans]) {
         int pos = lower_bound(f+1, f+ans+1, a[i].y) - f;
         f[pos] = a[i].y;
      }
   }
   printf ("%.15lf", 100*mhd-ans*(20-5*M_PI));
   return 0;
}

int youngore = thestars();

signed main() {;}

第三题

显然,三分图有以下性质:

  • 同一集合内,没有连边
  • 不存在一个点向同一个集合连两条边

发现上面的性质好像只涉及到两个集合啊,因此答案呼之欲出:\(calc(A,B)*calc(A,C)*calc(B,C)\)

状态:设\(f[i][j]\)表示一个集合有\(i\)个点,另一个集合有\(j\)个点的时候,炼成的三分图的个数

转移:\(f[i][j] = f[i][j-1]+f[i-1][j-1]*i\)

考虑第二个集合的最后一个点,他可能有什么情况,

第一种,不连边,继承上一次的

第二种,连边,则有\(i\)种连边方式,若练了某条边,则第一个集合对应的点就不能连其他边了,方案数为:\(i*f[i-1][j-1]\)

结果:\(f[a][b]*f[b][c]*f[a][c]\)

#include <bits/stdc++.h>
#define int long long
#define debug
using namespace std;

const int N = 3e3+66, mod = 998244353, M = 2000;

int t, a, b, c;
int f[N][N];

inline int thestars() {
   cin >> t;
   for (int i = 1; i <= 3000; ++ i) {
      f[i][1] = i+1;
      for (int j = 2; j <= i; ++ j) 
         f[i][j] = (f[i][j - 1] + i*f[i - 1][j - 1]%mod)%mod;
   }
   for (int i = 1; i <= 3000; ++ i)
      for (int j = 1; j < i; ++ j)
         f[j][i] = f[i][j];
         while(t --> 0) {
            int a, b, c;
            cin >> a >> b >> c;
            cout << f[a][b]*f[a][c]%mod*f[b][c]%mod << '\n';
         }
   return 0;
}

int youngore = thestars();

signed main() {;}

下午

讲dp!!!

我们来通过通过几道例题来体会一下什么是dp

01背包

将n件有着各自体积和价值的物品装入一个容积为m的背包中,求最大的总价值

状态:\(f[i][j]\)表示前\(i\)件物品放入背包为\(j\)的背包中所能获得的最大总价值

转移:\(f[i][j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i])\)

结果:\(f[n][m]\)

最长不下降子序列

给出一个长度为n 的序列,求最长的子序列,满足这些元素依次不减

状态:设\(f[i]\)表示以\(i\)为结尾的最长不下降子序列的最长长度

转移:\(f[i] = max(f[j]+1)\)其中\(a[j]<a[i]\)

结果:\(max(f[i])\)

上面是\(O(n^2)\)做法,还有一种\(O(nlogn)\)的做法:

状态:设\(f[i]\)表示长度为\(i\)的的最长不下降子序列的最后一个元素的最小值

举个例子:

有序列\(a[] = {8,2,3,5,7,6,4}\),所以可得:

\(f[0] = 0,f[1] = 2, f[2] = 3, f[3]=5,f[4]=7\)(第一遍扫)

遇到\(6\)考虑更新\(f[4]\),遇到\(4\)考虑更新\(f[3]\)

至于为什么是logn的,因为每次是二分查找(\(lower\)_\(bound\)是log的)

再次考虑\(dp\)的牛逼之处: 需要有易于表示的状态,能够列出的转移,便于计算的结果

要把式子推出来再去写代码

下面有几道一般dp,没什么特点,就是

渡河问题

click

状态:\(f[i]\)表示将前\(i\)头牛运到对岸的时间

转移:\(f[i] = min(f[j]+2*M+sum[i-j])\)其中\(sum\)为前缀和

结果:\(f[n]\)

解题

click

给出贪心的\(Hack\)数据:

50 5  
40 10  
10 40  
10 5  
10 3  
10 2

状态:\(f[i][j]\)表示处理\(1\)\(j\)的题,最后一次选择了\(i\)\(j\)的最短月数

转移:\(f[i][j] = min(f[k][i-1]+1/2)\)其中剩下的钱足够就是\(+1\)否则是\(+2\)

结果:\(min(f[i][p])+1\)

晚上

没时间写了啊,**白某飞主任

posted @ 2020-08-04 18:21  Youngore  阅读(80)  评论(0编辑  收藏  举报