2019牛客暑期多校训练营(第二场)(除C外)

本文精选了多项算法竞赛题目并提供了详细的题解分析,包括概率计算、搜索算法、矩阵优化等核心内容,适用于提高算法竞赛水平。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A Eddy walk

题意

给定长度为n的环,编号 0 , 1 , 2 , . . . . n − 1 0,1,2,....n-1 0,1,2,....n1,起始点在0,每一次可以向前向后走一格,问走完所有的格子之后所在的位置为M的概率。

分析

暴力打表找规律

const int maxn = 100 + 10;
double p[maxn];
int n;
bool vis[maxn];
void dfs(int x, int n, double pro) {
	if (pro < 1e-10 ) return ;
	for (int i = 0; i < n; ++i)
		if (!vis[i]) {
			int nxt = (x + 1) % n;
			bool tmp = vis[nxt];
			vis[nxt] = true;
			dfs(nxt, n, pro * 0.5);
			vis[nxt] = tmp;

			nxt = (x - 1 + n) % n;
			tmp = vis[nxt];
			vis[nxt] = true;
			dfs(nxt, n, pro * 0.5);
			vis[nxt] = tmp;
			return ;
		}
	p[x] += pro;
}
int main(void)
{
	int n = 6;
	for (int i = 1; i <= n; ++i) {
		for (int j = 0; j <= i; ++j)
			p[j] = 0;
		vis[0] = 1;
		dfs(0, i, 1);
		cout << i << endl;
		for (int j = 0; j < i; ++j) {
			cout << p[j] << " ";
		}
		cout << endl;
	}

	return 0;
}

B Eddy Walker 2

BM 算法套用即可

D Kth Minimum Clique

题意:

求第k小团

分析:

暴力搜索,每次加入一个节点, 使用优先队列每次取最小的团加入节点,使用bitset优化

#include<bits/stdc++.h>

using namespace std;
#define maxn 110
typedef long long ll;
int n, k, a[maxn];

bitset<maxn> bit[maxn];
struct node {
  bitset<maxn> now;
  ll val, last;
  bool operator <(const node &a) const {
    return val > a.val;
  }
};
ll solve() {
  priority_queue<node> q;
  node no;
  no.now.reset();
  no.val = 0;
  no.last = 0;
  q.push(no);
  while (!q.empty()) {
    node t = q.top(); q.pop();
    // cout << t.val << endl;
    if (--k == 0)
      return t.val;
    // cout << q.size() << endl;
    for (int i = t.last; i < n; ++i) {
      if (!t.now[i] && ((t.now & bit[i]) == t.now)) {
        t.now.set(i);
        // cout << t.val + a[i] << endl;
        // node nn = node{t.now, t.val + a[i], i + 1};
        // cout << nn.val << endl;
        q.push(node{t.now, t.val + a[i], i + 1});

        t.now.reset(i);
      }
    }
  }
  return -1;
}



int main(void)
{
  scanf("%d%d", &n, &k);
  for (int i = 0; i < n; ++i)
    scanf("%d", &a[i]);
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      int x; scanf("%1d", &x);
      bit[i].set(j, x);
      // cout << x << endl;
    }
  }
  printf("%lld\n", solve());
}


E MAZE

题意:

给定一个 N ∗ M N*M NM的01矩阵,0代表道路,1代表墙壁,每次可以从 ( i , j ) (i,j) (i,j)走到
( i + 1 , j ) , ( i , j + 1 ) , ( i , j − 1 ) (i+1,j),(i,j+1),(i,j-1) (i+1,j),(i,j+1),(i,j1),但不能走到墙壁也不能往回走,有Q次操作
操作 1:将 ( x , y ) (x,y) (x,y)位置反转,道路变为墙壁,墙壁变为道路
操作 2:询问从 ( 1 , x ) − &gt; ( n , y ) (1,x)-&gt;(n,y) (1,x)>(n,y)的合法道路有多少条

分析:

我们注意到N很大,M很小,可以通过矩阵实现行间状态转移,修改操作可以看做是修改转移矩阵 M,用线段树维护转移矩阵
M = M 1 ∗ M 2 ∗ . . . ∗ M N M = M_1*M_2*...*M_N M=M1M2...MN所有转移矩阵的乘积就是从第一行到最后一行的方案个数 M [ x , y ] M[x,y] M[x,y],就是我们所求。

参考代码:



const int maxn = 100+10;
bitset<maxn> bit[maxn];
int n,k;
int a[maxn];
char ar[maxn][maxn];
// 求第k小团

struct Node{
  int last;
  LL  val;
  bitset<maxn> bit;

  bool operator <(const Node &a)const{
    return val > a.val;
  }
};
LL solve(){
  priority_queue<Node> Q;
  Node node;
  node.val = node.last = 0;
  node.bit.reset();
  Q.push(node);
  while(!Q.empty()){
    Node tmp = Q.top(); Q.pop();
    if(--k == 0)
      return tmp.val;
    for(int i = tmp.last; i < n; ++i){
      if(!tmp.bit[i]&&(bit[i]&tmp.bit)==tmp.bit){
        tmp.bit.set(i);
        Q.push(Node{i+1,tmp.val+a[i],tmp.bit});
        tmp.bit.reset(i);   
      }
    }
  }
  return -1;
}
int main(void)
{
    cin>>n>>k;
    for(int i =0;i < n; ++i)
      cin>>a[i];
    for(int i = 0;i < n; ++i){
        cin>>ar[i];
        for(int j =0;j < n; ++j){
           // /if(ar[i] == '0')
            bit[i].set(j,ar[i][j]-'0');
          // else

        }
    }
    printf("%lld\n",solve());
   return 0;
}

F Partition problem

题意:

2 ∗ m 2*m 2m个人分到两个team,每个team右m个人,如果i,j两个人位于不同的team,那么收益增加 a [ i ] [ j ] a[i][j] a[i][j],求分配的最大收益

分析:

C ( 2 ∗ m , m ) C(2*m,m) C(2m,m) 暴力枚举+加预处理可过(评测机抖动的厉害)

参考代码:

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40981102

G Polygons

题意:

平面上有n条直线,求着n条直线形成的所有闭多边形的面积

分析:

PSLG(平面直线图)模板题,建议学习《算法竞赛入门经典训练指南》的板子

参考代码

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40979712

H Second Large Rectangle

题意:

求第二大子矩阵

分析:

利用单调栈求最大值,在最大值的基础上选择将 长度-1,宽度-1,取次大值

参考代码

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40973800

I Inside A Rectangle

题意:

给定一个 n ∗ m n*m nm的矩阵,矩阵元素 ∣ a i , j ∣ &lt; = 1 e 5 |a_{i,j}| &lt;=1e5 ai,j<=1e5, 最多可以选择两个矩形,求两个矩阵不想交部分的元素和的最大值

分析:

考虑所有情况即可,具体情况见代码以及注释
在这里插入图片描述

代码参考

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40981082

J Subarray

题意:

存在一个 [ 0 , 1 e 9 ) [0,1e9) [0,1e9)的数列,有n个区间值为1,其它的都为-1,求有多少个区间的值大于0

分析:

  1. 所有的1区间的长度的和为 ∑ l e n &lt; = 1 e 7 \sum len&lt;=1e7 len<=1e7,那么区间和大于0的区间数最大是 3 e 7 3e7 3e7
  2. 我们将所有的区间和可能大于0的区间都处理出来,怎么确定这样的区间呢?考虑判断两个1区间能否合并的条件 ( r [ i ] , l [ i + 1 ] ) (r[i],l[i+1]) (r[i],l[i+1])这段区间值为-1,我们需要从r[i] 向左的最大的区间和和l[i+1]向有的区间和大于 ( l [ i + 1 ] − r [ i ] − 1 ] ) (l[i+1]-r[i]-1]) (l[i+1]r[i]1]),所以预处理出来 从r[i]为右端点的区间最大值 p r e [ i ] pre[i] pre[i],以l[i] 为左端点的最大值 s u f [ i ] suf[i] suf[i]

p r e [ i ] = r [ i ] − l [ i ] + 1 + m a x ( p r e [ i − 1 ] − ( l [ i ] − r [ i − 1 ] − 1 ) , 0 ) ; pre[i] = r[i]-l[i]+1+max(pre[i-1]-(l[i]-r[i-1]-1),0); pre[i]=r[i]l[i]+1+max(pre[i1](l[i]r[i1]1),0);
s u f [ i ] = r [ i ] − l [ i ] + 1 + m a x ( s u f [ i + 1 ] − ( l [ i + 1 ] − r [ i ] − 1 ) , 0 ) suf[i] = r[i]-l[i]+1+max(suf[i+1]-(l[i+1]-r[i]-1),0) suf[i]=r[i]l[i]+1+max(suf[i+1](l[i+1]r[i]1),0)
3. 可以合并的区间一起处理,考虑移动右端点, s u m [ r ] sum[r] sum[r]要查询有多少左端点的前缀和小于 s u m [ r ] sum[r] sum[r],可以用树状数组来做,但是复杂度过高 O ( ∑ l e n l o g ( ∑ l e n ) ) O(\sum lenlog(\sum len)) O(lenlog(len)),考虑到 每次sum[i] 与sum[i-1] 的值差1,所以我们可以利用上一次计算的结果。
4. 假如当前值是1,小于 s u m [ r − 1 ] sum[r-1] sum[r1]的个数为 a n s ans ans个,当前的 s u m [ r ] = s u m [ r − 1 ] + 1 sum[r] = sum[r-1]+1 sum[r]=sum[r1]+1那么我们只需要加上 a n s + = c n t [ s u m [ r − 1 ] ] ans += cnt[sum[r-1]] ans+=cnt[sum[r1]]就可以了
5. 假设当前值是-1,小于 s u m [ r − 1 ] sum[r-1] sum[r1]的个数为 a n s ans ans个,当前的 s u m [ r ] = s u m [ r − 1 ] − 1 sum[r]=sum[r-1]-1 sum[r]=sum[r1]1那么我们需要减去 a n s − = c n t [ s u m [ r − 1 ] − 1 ] ans -= cnt[sum[r-1]-1] ans=cnt[sum[r1]1]

参考代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;
const int maxn = 1e6+10;
int inf = 1e9;
int l[maxn],r[maxn];
int suf[maxn],pre[maxn];// pre[i] 代表以 r[i] 为右端点的最大值是多少,suf[i] 代表以l[i] 为左端点的最大值是多少
const int maxm = 2e8+10;
bool vis[maxm];
int cnt[maxm];
int main(void){

  int n;cin>>n;
  for(int i = 1;i <= n; ++i){
    scanf("%d%d",&l[i],&r[i]);
  }
  pre[1] = r[1]-l[1]+1;
  for(int i = 2;i <= n; ++i){
    pre[i] = r[i]-l[i]+1+max(pre[i-1]-(l[i]-r[i-1]-1),0);
  }

  suf[n] = r[n]-l[n]+1;
  for(int i = n-1;i >= 1; --i){
    suf[i] = r[i]-l[i]+1+max(suf[i+1]-(l[i+1]-r[i]-1),0);
  }
  int k = 1;
  LL res = 0;
  while(k <= n){
    int j = k;
    while(j < n && (pre[j]+suf[j+1] >= l[j+1]-r[j]-1))
      ++j;
    int lb = max(0,l[k]-suf[k]),ub = min(inf-1,r[j]+pre[j]);


    for(int i = k;i <= j; ++i)
      for(int m = l[i];m <= r[i]; ++m)
        vis[m-lb] = 1;
    LL ans = 0;
    int t = 1e8;
    cnt[t] = 1;// 这里的t代表0
    for(int i = lb;i <= ub; ++i){
       if(vis[i-lb]) ans += cnt[t],cnt[++t]++;
       else ans -= cnt[t-1],cnt[--t]++;
       res += ans;
    }
    t = 1e8;
    for(int i = lb; i <= ub; ++i){
      if(vis[i-lb])
          cnt[++t]--;
      else
          cnt[--t]--;  
      if(vis[i-lb])
        vis[i-lb] = 0;      

    }
    k = j+1;
  }

  cout<<res<<endl;




  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值