2021西安邀请赛部分题解

G

题意:一棵树每个点权有一个w_iz可以作为u,v的中转站的条件如下。

  • zu,v之间的路径上。
  •  z可以是u,v之一。

  • u,v需要不同。

  • w_u - w_z \leqslant p

  • w_v - w_z \leqslant p

定义点z的承载量是z可以作为u,v的中转站的无序对(u,v)的个数。

最大化p使得每个点的承载量小于k

题解:二分p。将w升序,按顺序依次将每个点塞到按照dfs序建立的树状数组上。

有一个坑点:需要根据 zu,v之一, z不是u,v之一进行分类讨论,需要考虑清楚 zu,v之一这个case。

H

题意:有n种宝石,一次魔法可以把第i种宝石变成第j种宝石并且个数变成原来的a[i][j]倍。每次询问最少需要多少次魔法可以把1个第x种宝石变成2^k个第种宝石。

数据范围:询问次数是1000次。1\leqslant n \leqslant 100,1\leqslant k \leqslant 10^5,0\leqslant a[i][j]\leqslant 10

题解:

先用一个对数trick。

考虑矩阵快速幂A[t]][i][j]表示进行2^t次魔法后第j种宝石到第i种宝石的最大翻倍数。

每次询问需要从高位到低位拼出答案。用A[t]和向量res相乘。res的大小是n*1res[i]表示当前次数下第x种宝石到第i种宝石的最大翻倍数。需要保证当前次数下第x种宝石的翻倍数小于2^k。拼出的二进制数是ans。答案是ans+1即翻倍数不小于2^k

复杂度O(1000*20*n^2)

感想:确实是个好题,贴个代码。

#include<bits/stdc++.h>
using namespace std ;
typedef double db ;
typedef vector<db> vec ;
typedef vector<vec> mat ;
mat mul(mat &A , mat &B)
{
    mat C(A.size() , vec(B[0].size() , -1e9)) ;
    for(int i = 0 ; i < (int)A.size() ; i ++)
        for(int j = 0 ; j < (int)B[0].size() ; j ++)
            for(int k = 0 ; k < (int)A[0].size() ; k ++)
                C[i][j] = max(C[i][j] , A[i][k] + B[k][j]) ;
    return C ;
}
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n , q ;
    cin >> n >> q ;
    mat a(n , vec(n)) ;
    vector<mat> A(20 , mat(n , vec(n))) ;
    for(int i = 0 ; i < n ; i ++)
        for(int j = 0 ; j < n ; j ++)
            cin >> a[i][j] ;
    for(int i = 0 ; i < n ; i ++)
        for(int j = 0 ; j < n ; j ++)
        {
            if(a[j][i] == 0)  A[0][i][j] = -1e9 ;
            else  A[0][i][j] = log2(a[j][i]) ;
            if(i == j)  A[0][i][j] = 0 ;
        }
    for(int i = 1 ; i <= 19 ; i ++)  A[i] = mul(A[i - 1] , A[i - 1]) ;
    while(q --)
    {
        int x , k ;
        cin >> x >> k ;
        x -- ;
        mat res(n , vec(1 , -1e9)) ;
        res[x][0] = 0 ;
        int ans = 0 ;
        for(int i = 19 ; i >= 0 ; i --)
        {
            mat tmp = mul(A[i] , res) ;
            if(tmp[x][0] < k)  ans += (1 << i) , res = tmp ;
        }
        cout << ans + 1 << '\n' ;
    }
    return 0 ;
    //you should actually read the stuff at the bottom before submitting or in the confusion
}
/* stuff you should look for
    * long long
    * array bounds
    * init
    * ios
    * special cases (n=1?)
    * do smth instead of nothing and stay organized
    * WRITE STUFF DOWN
    * DON'T GET STUCK ON ONE APPROACH
    * DON'T GET STUCK ON ONE PROBLEM
*/

L

题意:每个点会发出两条射线,射线夹着的区域是合法区域。这样的点有n个。问n个合法区域交集的内接圆的半径。

题解:二分+半平面交。

感想:主要是这场没时间了,不会做这个的,即使是模板题。这场打的太差了。。。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值