• AtCoder abc176_f


    u1s1 最近比较鸽,被 whk 压榨干了……

    这是一个唯一的 ABC 的红题

    洛谷爬虫貌似还没修好,咕咕咕 & AtC 题目页面传送门

    有一列 (3n) 个数 (a_iin[1,n])。你需要进行 (n-1) 次操作,每次可以将最左边 (5) 个数任意排列,然后删除最左边 (3) 个数,如果这 (3) 个数相等则得 (1) 分。显然 (n-1) 次操作后还剩 (3) 个数,那么若它们相等则额外得 (1) 分。求最大得分。

    (nin[1,2000])

    由于一开始把「最左边」看成了「最右边」,就按照「最右边」想 & 写了,过不去样例然后发现了就 reverse 了一下(捂脸)。所以题解也按照「最右边」来写了。

    贪心什么的就不用想了,考虑 DP。

    注意到,第 (i) 次涉及到的 (5) 个数中,只有右边 (2) 个数也在第 (i-1) 次操作里被涉及到了,而左边 (3) 个数在前 (i-1) 次操作做完之后都是固定的((a_{3i-4sim 3i-2}))。于是就有了一个很显然的,以操作数为阶段的 DP:设 (dp_{i,j,k}) 表示考虑到第 (i) 次操作,第 (i) 次操作的右边 (2) 个数分别为 (j,k) 的最大得分。那么边界是 (dp_{1,i,j}=[a_1=i=j]),目标是 (dp_{n,a_{3n},a_{3n-1}})。转移的话,枚举就可以了,是(伪)(mathrm O(1)) 的:

    [dp_{i,j,k}=maxlimits_{vld(A(i,j,k),o,p)}{dp_{i-1,o,p}+eq(A(i,j,k)-{o,p})} ]

    其中 (A(i,j,k)={a_{3i-4},a_{3i-3},a_{3i-2},j,k})(vld(S,a,b)) 表示可重集 ({a,b}) 是否包含于可重集 (S)(eq(S)) 表示可重集 (S) 内的元素是否全部相等。

    这样总复杂度是 (mathrm O!left(n^3 ight)) 的,常数还比较大,大概 (3 imes5 imes5) 这样,显然过不去,考虑换思路?这辈子不可能的。考虑优化。

    注意到,光状态数量就爆炸了,于是想到 CF 809D 的套路,转移的时候直接在 DP 数组上修改来实现相邻阶段 DP 数组的转移,从而转化为一个 DS 向的问题(那题是平衡树维护的)。

    考虑分析这题的转移。显然可以分成 (3) 类:

    1. (o,p) 都是固定的 (3) 个中的:那么显然只有常数个,而且对于所有 DP 状态,它们都是合法转移。于是直接全局取 (max) 即可,对于有加 (1) 的单点取 (max) 特殊照顾一下;
    2. (o,p) 一个是固定的,一个不是固定的:不妨设 (o) 是固定的,(p) 不是固定的。注意到,由于 (o) 的数量是常数,所以 ((o,p)) 的数量是 (mathrm O(n))。而对于每个 ((o,p)),以它作为一个合法转移的状态在二维 DP 数组中显然分布成一行一列的并,于是对于每个 ((o,p)) 都做一次行取 (max) 和列取 (max),然后特殊情况一样特殊单点取 (max)
    3. (o,p) 都是固定的:那么显然对于每个状态,这个 ((o,p)) 唯一且就是它自身。于是如果固定的 (3) 个相等的话,全局加 (1) 即可。(这一种体现了那个套路的优势,即它可以将赋值转化为修改)

    总结一下,我们只需要维护一个数据结构,支持在二维的矩阵上进行单点取 (max)、行取 (max)、列取 (max)、全局加 (1)、单点查询(全局取 (max) 可以转化为 (n) 次行 / 列取 (max))。DS 学傻的同学们应该很容易想到平方乘以 1~2 个 (log) 的线段树,不过显然不会放你过去。

    事实上这是一个刚学 OI 的蒟蒻都会的。注意到一个点的 (max) 贡献来源只可能是单点、本行、本列。于是对于每个单点、行、列都维护一个取 (max) 的贡献总和,然后单点查询取三者 (max) 即可。还剩一个全局加 (1),那么显然在外面维护一个整体增加量即可,稍微动点脑子就能证明。

    这题中有些要注意的:如果真是个 DS 题,那么每次修改立即生效,而本题不然,它是以一个阶段为单位进行修改操作生效的。那怎么办?难道将 DP 数组 copy 一遍不成?那就梦回三方了。容易想到,对于每个修改先将要修改的地址和值存下来,等到本阶段结束之后一齐赋值。至于全局加 (1),应当在阶段开始时加,如果加成功了那么本阶段取值的时候就要减 (1)(太显然了吧)。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    #define X first
    #define Y second
    const int N=2000;
    int n;
    int a[3*N+1];
    int dp[N+1][N+1];
    int mx_r[N+1],mx_c[N+1];
    bool added;
    int real_dp(int x,int y){return max(dp[x][y],max(mx_r[x],mx_c[y]))-added;}
    vector<pair<int*,int> > chg;
    void chkmx(int &x,int y){chg.pb(mp(&x,y));}
    int main(){
    	cin>>n;
    	for(int i=1;i<=3*n;i++)scanf("%d",a+i);
    	reverse(a+1,a+3*n+1);//(捂脸 
    //	for(int i=1;i<=3*n;i++)cout<<a[i]<<" ";puts("");
    	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)dp[i][j]=a[1]==i&&i==j;
    	int add=0;
    	for(int i=2;i<=n;i++){
    		if(a[3*i-4]==a[3*i-3]&&a[3*i-3]==a[3*i-2])add++,added=true; 
    		else added=false;
    		chg.clear();
    		for(int j=3*i-4;j<=3*i-2;j++){
    			vector<int> v;
    			for(int k=3*i-4;k<=3*i-2;k++)if(k!=j)v.pb(a[k]);
    			for(int k=1;k<=n;k++)chkmx(mx_r[k],real_dp(v[0],v[1]));
    			chkmx(dp[a[j]][a[j]],real_dp(v[0],v[1])+1);
    		}
    		for(int j=1;j<=n;j++){
    			for(int k=3*i-4;k<=3*i-2;k++){
    				chkmx(mx_r[j],real_dp(j,a[k])),chkmx(mx_c[j],real_dp(j,a[k]));
    				vector<int> v;
    				for(int o=3*i-4;o<=3*i-2;o++)if(o!=k)v.pb(a[o]);
    				if(v[0]==v[1])chkmx(dp[j][v[0]],real_dp(j,a[k])+1),chkmx(dp[v[0]][j],real_dp(j,a[k])+1);
    			}
    		}
    		for(int j=0;j<chg.size();j++)*chg[j].X=max(*chg[j].X,chg[j].Y);
    	}
    	added=false;
    	cout<<real_dp(a[3*n],a[3*n-1])+add;
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    八、vue使用element-ui组件
    七、vue语法补充二(动态组件 & 异步组件、访问元素 & 组件、混入)
    oracle中使用sql语句生成10w条测试数据
    六、vue路由Vue Router
    五、vue状态管理模式vuex
    前端生态/工程化
    四、vue语法补充
    三、vue脚手架工具vue-cli的使用
    二、vue中组件的使用
    使用阿里云OSS上传文件
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/AtCoder-abc176-f.html
Copyright © 2020-2023  润新知