LINK:T2



这题感觉很套路 但是不会写.

区间操作 显然直接使用dp不太行 直接爆搜也不太行复杂度太高.

容易想到差分 由于使得整个序列都为0 那么第一个数也要i差分前一个数 强行加一个0 然后 显然让差分序列变成0即可。

每次可以单点修改两个位置的值 也可以当前和最后一个数后面那个数做 其实相当于单独做 表示后缀全体的事情。

0显然没有任何贡献了 考虑怎么做才是最优的。

当时没有证明 直接猜了一个结论是 每次操作比然会使一个位置上的值变成0.

(当时以为假了 结果时当时没有想清楚 自闭...

证明 如果存在一次操作使得两个数字没有一个变成0 那么在接下来的最优操作中 这个两个数字必然还会被选择 如果此时存在最优策略两次让4个数字变成0.

那么换成原来的 此时已经 拥有三次机会 让其中三个变成0 之后那第四个数字也会变成0 因为可以认为这四个数字时一组 最优需要三次。

那么 他们的总和确定 需要的值确定 其他三个变成后 剩下的值会让第四个值变成0.

至此可以发现前者策略造成的结果不差于后者.

还有一个小结论:

能一次消掉两个数字就直接消掉 因为从结果上看是当前最优.

对后续局面来看当前决策相对于其他决策也不会更差.

每次消的时候 可以发现 一个数字加上一个值 一个数字减掉一个值 这样做在%7意义下 之和为7.

那么1,6 2,5, 3,4 肯定是这些数字配对。

配对过后可以发现最多只剩下三种数字。

这个时候我选择了爆搜 因为 可能一些分组策略使得结果更优.

值得一提的是 这里爆搜复杂度海星 其实还可以更快就是爆搜多带一个属性为当前的值 正确性可以保证.

在数据随机的情况下确实可以得到60分 当时我以为写了个假代码(原来很稳..

const int MAXN=510;
int n,cnt,ans,num;
int a[MAXN],vis[MAXN],b[MAXN],w[MAXN],c[MAXN];
inline void dfs(int x,int v)
{
if(v>=ans)return;
if(x==num+1){ans=v;return;}
int mx;
rep(1,mod-1,i)if(b[i]){mx=i;break;}
--b[mx];dfs(x+1,v+1);
int ww=mod-mx;
if(b[ww])
{
--b[ww];
dfs(x+2,v+1);
++b[ww];
}
rep(1,mod-1,i)
{
if(b[i]&&i!=ww)
{
int cc=(i-ww+mod)%mod;
++b[cc];--b[i];
if(!cc)dfs(x+2,v+1);
else dfs(x+1,v+1);
--b[cc];++b[i];
}
}
++b[mx];
}
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
get(n);rep(1,n,i)get(w[i]);
rep(2,n,i)a[i]=w[i]-w[i-1];a[1]=w[1];
rep(1,n,i)rep(1,n,j)
{
if(!a[j]||!a[i])continue;
if(vis[i]||vis[j])continue;
if(a[i]==-a[j]||mod-a[i]==a[j]){vis[i]=vis[j]=1;++cnt;break;}
}
rep(1,n,i)if(!vis[i]&&a[i])++b[(a[i]+mod)%mod],++num;
ans=num;dfs(1,0);put(ans+cnt);return 0;
}

考虑优化.

容易想到这个爆搜的过程可以记忆化搜索 且 状态量不到1e8 可以直接搜。

可以直接dp f[i][j][k][w]表示前三种数字的数量以及当前积攒的值为w的最小操作次数.

这是后续了 我后来用了题解的状态 表示积攒的值为w的最大分组.

因为 我原本的想法时让每一个数字变成0 然后和其他数字配对可以产生一些效果.

而这里时让一些数字分组 每个组内最后一个数字是不需要被操作的。

两个状态等效 不过后者秒一点 每个组内和显然为7的倍数. 而前者也是如此.

值得注意的是 最后可能有一组不能恰好变成0 那么可以借用最后一个数字的后面的数字来做。

不过这组是不产生效果的。需要滚动数组来优化.

const int MAXN=510;
int n,cnt,ans,num;
int v1,v2,v3,c1,c2,c3;
int a[MAXN],w[MAXN],c[MAXN];
int f[2][MAXN][MAXN][7];//前i个x j个y k个z 当前大小为c所分的最多的组数.
inline int M(int x){return (x%mod+mod)%mod;}
inline void dp()
{
int u=0;
memset(f,-1,sizeof(f));
f[0][0][0][0]=0;
rep(0,c1,i)
{
u=u^1;
rep(0,c2,j)rep(0,c3,k)rep(0,mod-1,s)f[u][j][k][s]=-1;
rep(0,c2,j)
rep(0,c3,k)
{
rep(0,mod-1,s)
{
if(f[u^1][j][k][s]==-1)continue;
if(i+1<=c1)
{
int ww=M(v1+s);
f[u][j][k][ww]=max(f[u][j][k][ww],f[u^1][j][k][s]+(s==0));
}
if(j+1<=c2)
{
int ww=M(v2+s);
f[u^1][j+1][k][ww]=max(f[u^1][j+1][k][ww],f[u^1][j][k][s]+(s==0));
}
if(k+1<=c3)
{
int ww=M(v3+s);
f[u^1][j][k+1][ww]=max(f[u^1][j][k+1][ww],f[u^1][j][k][s]+(s==0));
}
}
}
}
rep(0,mod-1,s)ans=max(ans,f[u^1][c2][c3][s]-(s!=0));
}
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
get(n);rep(1,n,i)get(w[i]);
rep(2,n,i)a[i]=w[i]-w[i-1];a[1]=w[1];
rep(1,n,i)a[i]=M(a[i]),++c[a[i]];
int w1=min(c[1],c[6]);cnt+=w1;
if(c[1]>c[6])c1=c[1]-w1,v1=1;else c1=c[6]-w1,v1=6;
w1=min(c[2],c[5]);cnt+=w1;
if(c[2]>c[5])c2=c[2]-w1,v2=2;else c2=c[5]-w1,v2=5;
w1=min(c[3],c[4]);cnt+=w1;
if(c[3]>c[4])c3=c[3]-w1,v3=3;else c3=c[4]-w1,v3=4;
dp();cnt=cnt+c1+c2+c3-ans;put(cnt);
return 0;
}

5.12 省选模拟赛 T2 贪心 dp 搜索 差分的更多相关文章

  1. 4.12 省选模拟赛 LCA on tree 树链剖分 树状数组 分析答案变化量

    LINK:duoxiao OJ LCA on Tree 题目: 一道树链剖分+树状数组的神题. (直接nQ的暴力有50. 其实对于树随机的时候不难想到一个算法 对于x的修改 暴力修改到根. 对于儿子的 ...

  2. 5.10 省选模拟赛 拍卖 博弈 dp

    LINK:拍卖 比赛的时候 前面时间浪费的有点多 写这道题的时候 没剩多少时间了. 随便设了一个状态 就开始做了. 果然需要认真的思考.其实 从我的状态的状态转移中可以看出所有的结论. 这里 就不再赘 ...

  3. 4.3 省选模拟赛 序列游戏 dp

    可以发现 某一段被删除后状态难以表示 也难以链接起来. 考虑暴力 有40分的状压dp 暴力存状态 然后枚举转移即可.最后注意和f[0]这个状态取max 不然一分都没有. const int MAXN= ...

  4. 6.3 省选模拟赛 Decompose 动态dp 树链剖分 set

    LINK:Decompose 看起来很难 实际上也很难 考验选手的dp 树链剖分 矩阵乘法的能力. 容易列出dp方程 暴力dp 期望得分28. 对于链的情况 容易发现dp方程可以转矩阵乘法 然后利用线 ...

  5. 5.10 省选模拟赛 tree 树形dp 逆元

    LINK:tree 整场比赛看起来最不可做 确是最简单的题目. 感觉很难写 不过单独考虑某个点 容易想到树形dp的状态. 设f[x]表示以x为根的子树内有黑边的方案数. 白边方案只有一种所以不用记录. ...

  6. 4.13 省选模拟赛 树 树形dp 卷积 NTT优化dp.

    考试的时候 看到概率 看到期望我就怂 推了一波矩阵树推自闭了 发现 边权点权的什么也不是. 想到了树形dp 维护所有边的断开情况 然后发现数联通块的和再k次方过于困难. 这个时候 应该仔细观察一下 和 ...

  7. 【洛谷比赛】[LnOI2019]长脖子鹿省选模拟赛 T1 题解

    今天是[LnOI2019]长脖子鹿省选模拟赛的时间,小编表示考的不怎么样,改了半天也只会改第一题,那也先呈上题解吧. T1:P5248 [LnOI2019SP]快速多项式变换(FPT) 一看这题就很手 ...

  8. 模拟赛T2 交换 解题报告

    模拟赛T2 交换 解题报告 题目大意: 给定一个序列和若干个区间,每次从区间中选择两个数修改使字典序最小. \(n,m\) 同阶 \(10^6\) 2.1 算法 1 按照题意模拟,枚举交换位置并比较. ...

  9. @省选模拟赛03/16 - T3@ 超级树

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 一棵 k-超级树(k-SuperTree) 可按如下方法得到:取 ...

随机推荐

  1. Git篇----创建远程仓库

    现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举 ...

  2. adb devices 不能连接设备 could not install *smartsocket* listener

    cmd以管理员身份运行命令adb devices  或adb reverse tcp:8081 tcp:8081,无法连接设备,出现上图信息. 输入命令:adb kill-server 再输入:adb ...

  3. nginx中的root和alias辨析

    root介绍 Syntax: root path; Default: root html; Context: http, server, location, if in location Sets t ...

  4. 题解:2018级算法第六次上机 C6-不Nan的过河

    题目描述: 样例: 实现解释: 一道因为没排序做了一个小时没做出来的二分答案模板题(手动呲牙) 知识点: 二分答案,最大值最小化 坑点: 排序,judge(mid)函数内计数的实现 其实从最长一步的最 ...

  5. Redis之字典

    概念 字典,又称为符号表.关联数组或映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构.字典中每个键都是独一无二的,程序可以根据键来更新值,或者删除整个键值对. 用途 ...

  6. 题解 CF296B 【Yaroslav and Two Strings】

    题目 传送门 题目大意 如果两个只包含数字且长度为 \(n\) 的字符串 \(s\) 和 \(w\) 存在两个数字 \(1≤i,j≤n\),使得 \(s_i<w_i,s_j>w_j\) , ...

  7. JAVA面向对象:三大特征 封装讲解

    一.JAVA封装 1.封装的理解 封装是 JAVA 面向对象思想的 一 种特性,也是一种信息隐蔽的技术 2.封装的原则 将类中的某些信息隐藏起来,来防止外部程序直接访问,通过类中的方法实现对隐藏的信息 ...

  8. 不懂DevOps!他在升职加薪的那天下午,提出了离职

    不久前我们一个已毕业的学员向班主任老师分享了前几天他遇到的一件事: 一个许久未联系他的朋友突然打电话给他,寒暄了几句后突然说,想来北京找工作,问能不能帮忙给介绍一些工作. 在接下来的通话中,我们学员了 ...

  9. Git 推送到远程仓库

    github:https://github.com/ 国内的:https://gitee.com/ (和Github非常相似的) 一.Http方式进行推送 右击同步,配置远端,将URL替换成远程仓库的 ...

  10. STL源码剖析:配接器

    启 配接器就是适配器 STL中的适配器一共三种: 迭代器适配器 是一种观念上的改变,如将赋值操作变成插入,前进变成后退,等 函数适配器 STL中最广泛的配接器群体 可以实现连续配接 配接操作:bind ...