2020牛客暑期多校训练营(第五场)

比赛链接

A

一、题意

n个点m条带权边的无向连通图。边权就是距离。

初始时你在点1,让你依次经过2*k个点,使总距离最小。

定义一个瞬移方式:

(1)你可以从一个传送门花费0距离到达另一个传送门。

(2)初始时每个点都有一个关闭的传送门。

(3)你可以在任何一个点关闭任何一个传送门。

(4)你只能打开当前点的传送门。

(5)同时最多存在两个开启的传送门。

数据范围:n,k\leqslant 300

二、题解

只关注一个传送门的位置即可,因为我们可以在需要的时候在当前位置开启一个传送门,到达另一个传送门。

dp[i][j]表示到达第i个点时其中一个传送门在j。

转移方式在注释中,就是把ppt的题解模拟了一下。

三、代码

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << '*' << x << '\n'
#define ddebug(x , y)  cerr << '*' << x << ' ' << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
const int mod = 998244353 ;
const int maxn = 300 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
mt19937  rnd(chrono::high_resolution_clock::now().time_since_epoch().count()) ; 
int n , m , p ;
int c[maxn << 1] ;
ll dis[maxn][maxn] ;
ll dp[maxn << 1][maxn] ;
int main()
{
    ios ;
    cin >> n >> m >> p ;
    mem_inf(dis) ;
    mem_inf(dp) ;
    rep(i , 1 , m)
    {
        int u , v ;
        ll w ;
        cin >> u >> v >> w ;
        dis[u][v] = min(dis[u][v] , w) ;
        dis[v][u] = min(dis[v][u] , w) ; 
    }
    rep(i , 1 , n)  dis[i][i] = 0ll ;
    rep(k , 1 , n)  rep(i , 1 , n)  rep(j , 1 , n)  dis[i][j] = min(dis[i][j] , dis[i][k] + dis[k][j]) ;
    p = p * 2 ;
    rep(i , 1 , p)  cin >> c[i] ;
    dp[0][1] = 0 ;
    c[0] = 1 ;
    rep(i , 0 , p - 1)  rep(j , 1 , n)
    {
        //1.直接从c[i]走到c[i+1]
        dp[i + 1][j] = min(dp[i + 1][j] , dp[i][j] + dis[c[i]][c[i + 1]]) ;
        //2.枚举走到c[i+1]之后,传送门的位置变为了哪个节点,设这个节点是k。第二种转移是从c[i]走到k,在k设置传送门,从k传送到j,再从j走到c[i+1]
        rep(k , 1 , n)  dp[i + 1][k] = min(dp[i + 1][k] , dp[i][j] + dis[c[i]][k] + dis[j][c[i + 1]]) ;
        //3.第三种转移是从c[i]传送到j,从j走到k,在k设置传送门,最后从k走到c[i+1]
        rep(k , 1 , n)  dp[i + 1][k] = min(dp[i + 1][k] , dp[i][j] + dis[j][k] + dis[k][c[i + 1]]) ;
    }
    ll ans = 1e18 ;
    rep(i , 1 , n)  ans = min(ans , dp[p][i]) ;
    cout << ans << '\n' ;
    return 0 ;
}

B

一、题意

二、题解

三、代码

C

一、题意

二、题解

三、代码

D

一、题意

二、题解

三、代码

E

一、题意

二、题解

三、代码

 

F

一、题意

二、题解

三、代码

G

一、题意

二、题解

三、代码

H

对着这个学的:https://blog.nowcoder.net/n/a149ac86c99b4e99953c2528fd4de247

主席树空间多开点,我开了200倍。

代码

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
const int mod = 998244353 ;
const int maxn = 2e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
mt19937  rnd(chrono::high_resolution_clock::now().time_since_epoch().count()) ; 
int n , a[maxn] ;
map<int , int> last ;
int sum[maxn << 2] ;
int ls(int x)
{
   return x << 1 ;
}
int rs(int x)
{
   return x << 1 | 1 ;
}
void update(int id , int l , int r , int x , int y)
{
   int mid = (l + r) / 2 ;
   if(l == r && l == x){sum[id] = y ; return ;}
   if(x <= mid)  update(ls(id) , l , mid , x , y) ;
   else  update(rs(id) , mid + 1 , r , x , y) ;
   sum[id] = sum[ls(id)] & sum[rs(id)] ;
}
int query(int id , int l , int r , int x , int y)
{
   int ans = (1 << 30) - 1 ;
   int mid = (l + r) / 2 ;
   if(y < x)  return 0 ; //不合法的询问
   if(x <= l && r <= y)  return sum[id] ;
   if(x <= mid)  ans &= query(ls(id) , l , mid , x , y) ;
   if(y > mid)  ans &= query(rs(id) , mid + 1 , r , x , y) ;
   return ans ;
}
struct Tree
{
    int lson[maxn * 200] , rson[maxn * 200] , sum[maxn * 200] ;
    int cnt , root[maxn] ;
    void init()
    {
        cnt = 0 ;
        mem0(lson) , mem0(rson) ;
        mem0(root) , mem0(sum) ;
    }
    void update(int l , int r , int pre , int &now , int x , int y)
    {
        cnt ++ ;
        lson[cnt] = lson[pre] , rson[cnt] = rson[pre] , sum[cnt] = sum[pre] ;
        now = cnt , sum[now] += y ;
        if(l == r) return ;
        int mid = (l + r) >> 1 ;
        if(x <= mid)  update(l , mid , lson[pre] , lson[now] , x , y) ;
        else  update(mid + 1 , r , rson[pre] , rson[now] , x , y) ;
    }
    int query(int id , int l , int r , int x , int y)
    {
       int ans = 0 ;
       int mid = (l + r) / 2 ;
       if(y < x)  return 0 ; //不合法的询问
       if(x <= l && r <= y)  return sum[id] ;
       if(x <= mid)  ans += query(lson[id] , l , mid , x , y) ;
       if(y > mid)  ans += query(rson[id] , mid + 1 , r , x , y) ;
       return ans ;
    }
} tree ;
vector<pii> v ;
void init(int i)
{
    cl(v) ;
    int now = i ;
    int x = a[i] ;
    while(now >= 1)
    {
        int l = 1 , r = now ;
        int p = l ;
        while(l <= r)
        {
            int mid = (l + r) / 2 ;
            if(query(1 , 1 , n , mid , i) == x)  p = mid , r = mid - 1 ;
            else  l = mid + 1 ;
        }
        v.pb({x , now}) ;
        now = p - 1 ;
        if(now >= 1)  x = query(1 , 1 , n , now , i) ;
    }
}
void prework()
{
    tree.init() ;
    rep(i , 1 , n)
    {
        init(i) ;
        tree.root[i] = tree.root[i - 1] ;
        for(auto u : v)
        {
            int x = u.fi ;
            int p = u.se ;
            if(last.count(x))  tree.update(1 , n , tree.root[i] , tree.root[i] , last[x] , -1) ;
            last[x] = p ;
            tree.update(1 , n , tree.root[i] , tree.root[i] , p , 1) ;
        }
    }
}
int main()
{
    ios ;
    cin >> n ;
    rep(i , 1 , n)  cin >> a[i] , update(1 , 1 , n , i , a[i]) ;
    prework() ;
    int q , ans = 0 ;
    cin >> q ;
    while(q --)
    {
        int l , r ;
        cin >> l >> r ; 
        l = (l ^ ans) % n + 1 ;
        r = (r ^ ans) % n + 1 ;
        if(l > r)  swap(l , r) ;
        ans = tree.query(tree.root[r] , 1 , n , l , r) ;
        cout << ans << '\n' ;
    }
    return 0 ;
}

I

一、题意

二、题解

三、代码

J

一、题意

二、题解

三、代码

K

一、题意

二、题解

三、代码

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值