首页 > 其他分享 >Skills - 2022 International Collegiate Programming Contest, Jinan Site, Problem J.

Skills - 2022 International Collegiate Programming Contest, Jinan Site, Problem J.

时间:2024-10-02 22:11:44浏览次数:6  
标签:状态 Contest Skills neg Jinan 学习 int using 技能

有3种技能,\(n(\le 10^3)\)天内每天可以对一个技能进行学习,第i天学习第j个技能可以为第j个技能增加\(a_{i,j}(\le 10^4)\)的熟练度。在第i天结束时,每个技能的熟练度会减去距离上次学习该技能的天数,但最多减到0。求n天后能得到的熟练度的和的最大值。

首先容易有一个显然的dp状态:\(f_{i,j,k}\) 表示三种技能上次被学习的天数,然后枚举接下来学哪个技能进行转移,这是 \(O(n^3)\) 的。

考虑减少状态数。注意到 \(a\) 的范围是比较小的,这意味着,一天能够获得的最大收益不会很高,那么也就是说长时间放置一个技能不去学习造成的亏损可能大过收益。具体的来说,假设技能 1 已经有 t 天没有学习,如果我放弃 s 天前的收益转而去学技能 1,那么我就会少亏 \(s(t-s)\)。所以一个技能一旦开始学习就不会放置超过 \(2\sqrt{a} +O(1)\) 天,状态数就减少到了 \(O(na)\)。

此题可以用斜率优化做到 \(O(n^2)\)。提示,若只有 2 种技能如何用 \(O(n)\) 解决?

2 种技能时,直接做状态是 \(O(n^2)\) 转移是 \(O(1)\),我们平衡一下状态和转移。我们在状态中只记录当前的天数和今天学了哪个技能,\(f_{i,t=0/1}\) 表示第 \(i\) 天学了技能 \(t\),并钦定接下来一定学习一段时间的 \(\neg t\),这是因为如果接下来还学 \(t\),就需要知道上一次学 \(\neg t\) 是什么时候来计算扣多少熟练度,这在我们的状态中并没有记录;而如果强制学 \(\neg t\),则不需要知道这个信息。于是转移需要枚举 \(\neg t\) 要往后学到什么时候,变成了 \(O(n)\) 的转移。写出递推式后可以斜率优化到 \(O(1)\) 转移。

3 种技能时,我们延续上述思路。\(f_{i,j,t}\) 表示当前在第 \(i\) 天,一个技能上次学习在第 \(i\) 天,一个在第 \(j\) 天,还有一个在第 \(k\) 天但未放入状态,\(t\) 表示 \(i\),\(j\) 分别是第几个技能。因为 \(k\) 未被放入状态,所以钦定接下来学习一段时间的技能 \(k\)。转移时分 \(k=0\),\(0<k<j\) 和 \(j<k<i\) 的三种情况并注意 \(j=0\) 的处理,后两种维护凸包进行斜率优化即可做到 \(O(n^2)\)。

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;
using pii=pair<int,int>;
#define endl '\n'
#define pL p<<1,l,mid
#define pR p<<1|1,mid+1,r
#define all(x) x.begin(),x.end()
void solve();
void init();
int main(){
	init();
	int t=1;
	cin>>t;
	while(t--)solve();
}

void init(){
    #ifdef ONLINE_JUDGE
    cin.tie(nullptr)->ios::sync_with_stdio(false);
	cout.tie(nullptr);
    #endif

}
const int N=1003,inf=1e9;
int s[N][3];
struct slope_opt{
    int x[N],y[N],h,t;
    void clear(){
        h=t=1;
    }
    void add(int x0,int y0){
        while(t-h>=2&&1ll*(y0-y[t-2])*(x0-x[t-1])<=1ll*(y0-y[t-1])*(x0-x[t-2]))--t;
        x[t]=x0;y[t]=y0;
        ++t;
    }
    int query(int k){ // k decend
        while(t-h>=2&&1ll*k*(x[h+1]-x[h])<=(y[h+1]-y[h]))++h;
        if(t==h)return -inf;
        return y[h]-k*x[h];
    }
}A[6],B[6];

int f[N][N][6];
int wa[]={5,3,4,1,2,0};
int wb[]={3,5,1,4,0,2};
int ts[]={0,0,1,1,2,2};
int fun(int x){
    return x*(x+1)/2;
}
void solve(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        int a0,a1,a2;
        cin>>a0>>a1>>a2;
        s[i][0]=s[i-1][0]+a0;
        s[i][1]=s[i-1][1]+a1;
        s[i][2]=s[i-1][2]+a2;
    }
    

    int ans=0;
    for(int i=1;i<=n;i++)
        for(int t=0;t<6;t++){
            A[t].add(i-1,f[i-1][0][wa[t]]-s[i-1][ts[t]]-fun(i-2));
            int res=A[t].query(-i)+s[i][ts[t]]-fun(i);
            f[i][0][t]=max(s[i][ts[t]],res);
            if(i==n)ans=max(ans,f[i][0][t]);
        }
    for(int j=1;j<=n-1;j++){
        for(int t=0;t<6;t++){
            A[t].clear();
            B[t].clear();
        }
        for(int k=1;k<j;k++){
            for(int t=0;t<6;t++){
                B[t].add(k,fun(j-k)-fun(k-1)+f[j][k][wb[t]]);
            }
        }
        for(int i=j+1;i<=n;i++){
            for(int t=0;t<6;t++){
                if(i-j>1)A[t].add(i-1,fun(i-1-j)-s[i-1][ts[t]]-fun(i-2)+f[i-1][j][wa[t]]);
                int res=max(A[t].query(-i),B[t].query(-i)-s[j][ts[t]])-fun(i);
                f[i][j][t]=max(res,f[j][0][wb[t]]-s[j][ts[t]])+s[i][ts[t]]-fun(i-j);
                if(i==n)ans=max(ans,f[i][j][t]);
            }
        }
    }
    cout<<ans<<endl;
}

标签:状态,Contest,Skills,neg,Jinan,学习,int,using,技能
From: https://www.cnblogs.com/nkxjlym/p/18445173

相关文章

  • AtCoder Beginner Contest 373
    省流版A.暴力即可B.求出字母位置,绝对值相加即可C.显然答案为两个数组的最大值的和D.注意直接BFS的点权范围不超过题目范围,直接BFS即可E.发现单调性,二分票数,用前缀和\(O(1)\)判断可行性即可F.朴素背包DP,相同重量的物品一起考虑,用优先队列求解\(l\)个相同重量物品最大......
  • The 2023 ICPC Asia Macau Regional Contest
    A.(-1,1)-Sumplete首先只取\(-1\),这样的话选1和不选-1产生的贡献就是都是+1。枚举每一行,然后贪心选择需求最大的若干列。#include<bits/stdc++.h>usingnamespacestd;usingi32=int32_t;usingi64=longlong;#defineinti64usingvi=vector<int>;usingpi......
  • The 2024 Guangdong Provincial Collegiate Programming Contest
    Preface这场据说题挺毒的?但实际打的时候感觉也还好,3h就出了7个题,然后被H卡飞了赛后发现是没有观察到构造的解的性质,把Dinic换成匈牙利就过了,只能说对flow的理解不够B.腊肠披萨神秘string题,被徐神开场一眼秒了,虽然中间我和祁神上去写了三个签到,但徐神还是在1h不......
  • AtCoder Beginner Contest 365
    A-LeapYear思路模拟即可;AC代码#include<bits/stdc++.h>#defineendl'\n'#defineintintlonglong#definepbpush_back#definebsbitsetusingnamespacestd;typedefpair<char,int>PCI;typedefpair<......
  • ABC 模拟赛 | A Passing Contest 001题解记录(A,B,C,D)
    比赛链接https://www.luogu.com.cn/contest/126344[APC001]A-CT不必多说,多次取模#include<iostream>#include<algorithm>#include<string>#include<string.h>#include<queue>#include<deque>#include<math.h>#include<map>......
  • The 2024 ICPC Asia East Continent Online Contest (II)
    A.GamblingonChoosingRegionals最差情况就是,强队都和你去一起。因此赛站越小,排名也一定越小。然后只要动态实现出每个学校最强的若干只队伍就好了。#include<bits/stdc++.h>usingnamespacestd;usingi32=int32_t;usingi64=longlong;#defineinti64using......
  • 题解:AtCoder Beginner Contest AT_abc373_d ABC373D Hidden Weights(格式美化版)
    题目传送门题目翻译给你一个NNN个点,MMM条边的有向图,其中边有边......
  • The 2023 ICPC Asia Nanjing Regional Contest / The 2nd Universal Cup. Stage 11: N
    比赛链接I.Counter按时间排序即可,注意可以不清零。F.EquivalentRewriting对于每个位置,把所有有这个位置的操作编号连向这个位置最终的值,做个拓扑排序,看看字典序最大的即可。复杂度\(\Theta(n+m)\)。C.PrimitiveRoot枚举和\(m\)的公共前缀,设\(i\)位置\(m\)是\(1......
  • AtCoder Beginner Contest 365题解
    A-LeapYear按照题意模拟即可。codeB-SecondBest按照题意模拟即可。codeC-TransportationExpenses考虑当\(x\)增大时,\(\min(x,a_i)=x\)的项会越来越少。换言之,当\(x\)足够大时,\(ans=\suma_i\),若此时\(ans>M\)则说明无论补贴多少,这时答案都是一定的......
  • AtCoder Beginner Contest 371(ABCDE)
    A个人直接硬解,讨论情况也并不复杂代码:#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e6+10;voidsolve(){chara,b,c;cin>>a>>b>>c;if(a=='<'){if(c=='<......