USACO2023Feb游记
由于学校要求,过来打 USACO。
由于上次已经打到白金了,所以继续。
然后还是 AK 了。
感觉题意很迷惑,所以都翻译一下。
Hungry Cow
Bessie 很饿,每天晚饭如果有干草就会吃 \(1\) 份,没有就不吃,初始没有干草。
每天早上 Farmer John 会给它送若干干草,设第 \(k\) 天送 \(a_k\) 份干草,初始时 \(a_k=0\),表示该天不送干草。
\(q\) 次操作,每次给出 \(x,y\),表示将 \(a_x\) 改成 \(y\),请将在此时 Bessie 有干草吃的日期编号求和并输出。对 \(10^9+7\) 取模。
操作间互不独立。
\(1\le q\le10^5\),\(1\le x\le10^{14}\),\(0\le y\le10^9\)。
注意到这个过程是一个“前时间”对“后时间”的贡献,考虑兔队线段树。
维护区间内有干草吃的编号数 \(cnt\)、编号和 \(ans\),以及对区间外的贡献 \(more\)。
单点修改,全局查询。
考虑计算左侧区间对当前节点带来 \(c\) 的前缀贡献时,区间内三个值的值。
当填满左儿子空位时,就不用向左递归;否则,只用向左递归,右儿子结果和 \(c=0\) 时的右儿子结果一致。
总复杂度 \(O(q\log^2v)\)。
核心代码:
const ullt Mod=1e9+7,inv2=5e8+4;
typedef ConstMod::mod_ullt<Mod>modint;
typedef std::vector<modint>modvec;
inline modint sum(ullt l,ullt r){return modint(r-l)*(l+r-1)*inv2;}
struct Seg{
Seg*L,*R;ullt id,len,cnt,more;modint ans;
Seg(ullt l,ullt r):L(NULL),R(NULL),id(l),len(r-l),cnt(0),more(0),ans(){}
voi chg(ullt p,ullt v){
if(len==1){
if(v)cnt=1,more=v-1,ans=id;else cnt=more=0,ans=0;
return;
}
if(p<(len>>1)){
if(!L)L=new Seg(id,id+(len>>1));
L->chg(p,v);
}
else{
if(!R)R=new Seg(id+(len>>1),id+len);
R->chg(p-(len>>1),v);
}
ans=L?L->ans:0,cnt=L?L->cnt:0;
ullt Lv=L?L->more:0;
auto g=R?R->cal(Lv):std::pair<modint,std::pair<ullt,ullt> >
{sum(id+(len>>1),id+std::min(len,Lv+(len>>1))),
{std::min(len-(len>>1),Lv),Lv-std::min(len-(len>>1),Lv)}};
ans+=g.first,cnt+=g.second.first,more=g.second.second;
}
std::pair<modint,std::pair<ullt,ullt> >cal(ullt c){
if(!c)return{ans,{cnt,more}};
if(c+cnt>=len)return{sum(id,id+len),{len,c+cnt-len+more}};
// printf("%llu %llu\n",id,id+len);
auto g1=L?L->cal(c):std::pair<modint,std::pair<ullt,ullt> >
{sum(id,id+std::min(len>>1,c)),{std::min(len>>1,c),c-std::min(len>>1,c)}};
ullt Lv=g1.second.second;
if(Lv==(L?L->more:0))return{ans-(L?L->ans:0)+g1.first,{g1.second.first+cnt-(L?L->cnt:0),more}};
auto g2=R?R->cal(Lv):std::pair<modint,std::pair<ullt,ullt> >
{sum(id+(len>>1),id+std::min(len,Lv+(len>>1))),
{std::min(len-(len>>1),Lv),Lv-std::min(len-(len>>1),Lv)}};
return{g1.first+g2.first,{g1.second.first+g2.second.first,g2.second.second}};
}
};
Problem Setting
Farmer John 出了 \(n\) 道题,聘了 \(m\) 个验题人来品鉴难度。
难度只有简单(E)和困难(H)两种。
Farmer John 将从中选出若干道(至少一道),并以一定顺序排列,使得前一道题目中任意一个觉得此题困难的验题人也觉得后面一道题目困难。
回答有多少种选出来并排列的方案,对 \(10^9+7\) 取模。
\(1\le n\le10^5\),\(1\le m\le20\)。
考虑把 E 视作 \(0\),H 视作 \(1\),就是相邻项为子集包含。
考虑把状态相同的数一起考虑,假设出现了 \(t\) 次,选择其的方案数即为
\]
可以 \(O(n)\) 预处理。
假设选的最后一个集合为 \(S\) 的方案数为 \(f_S\),集合为 \(S\) 的元素有 \(a_S\) 个,则
\]
直接子集枚举是 \(O(3^m)\) 的,不优,考虑更优做法。
注意到这个东西是一个半半在线卷积的形式,直接按 \(|S|\) 递增序进行 FMT 即可。其实就是高维前缀和。
总复杂度 \(O(nm+m^22^m)\),可以通过。
听说有 \(O(nm+m2^m)\) 做法,不懂哦,可能因为这里是子集和的形式吧。
核心代码:
const ullt Mod=1e9+7;
typedef ConstMod::mod_ullt<Mod>modint;
typedef std::vector<modint>modvec;
chr C[20][100005];uint Cnt[1u<<20|1],nLim;
modint P[100005],Q[100005],G[100005],Dp[21][1u<<20|1],User[1u<<20|1];
voi FWT(modint*x,bol op){
for(uint i=1;i<nLim;i<<=1)for(uint j=0;j<nLim;j+=i<<1)for(uint k=0;k<i;k++)
op?x[i+j+k]-=x[j+k]:x[i+j+k]+=x[j+k];
}
int main()
{
#ifdef MYEE
freopen("QAQ.in","r",stdin);
// freopen("QAQ.out","w",stdout);
#endif
uint n,m;scanf("%u%u",&n,&m);
for(uint i=0;i<m;i++)scanf("%s",C[i]);
for(uint i=0;i<n;i++){
uint v=0;
for(uint j=0;j<m;j++)if(C[j][i]=='H')v|=1u<<j;
Cnt[v]++;
}
P[0]=1;for(uint i=1;i<=n;i++)P[i]=P[i-1]*i;
Q[n]=P[n].inv();for(uint i=n;i;i--)Q[i-1]=Q[i]*i;
for(uint i=0;i<n;i++)G[i+1]=G[i]+Q[i];
for(uint i=0;i<=n;i++)G[i]*=P[i];
modint ans;
nLim=1u<<m;
for(uint j=0;j<=m;j++){
for(uint k=0;k<(1u<<m);k++)if(__builtin_popcount(k)==j){
Dp[j][k]=1;
for(uint t=0;t<j;t++)Dp[j][k]+=Dp[t][k];
ans+=Dp[j][k]*=G[Cnt[k]];
}
FWT(Dp[j],0);
}
ans.println();
return 0;
}
Watching Cowflix
Bessie 喜欢在 Cowflix 上看节目,并且喜欢在农场里的不同地方看。
Farmer John 的农场可以被描述成一颗 \(n\) 个节点的树,并且 Bessie 只可能在树上的一些指定的节点处看节目。每个节点是否要看节目将在初始时给定;保证至少在一个节点处会看节目。
不幸的是,Cowflix 为了避免奶牛们使用公用账号,采取了一个新的会员策略:
Bessie 将多次付款,每次选择树上任意一个大小为 \(d\) 的联通块,为其支付 \(d+k\) 的代价,才能够在这些位置看节目。
换言之,Bessie 将选取若干联通块 \(c_1,c_2,\dots,c_C\),支付 \(\sum_{i=1}^C(|c_i|+k)\) 的代价,才可以在这些联通块的各个节点处看节目;即,被指定的节点必须被某个联通块包含,不被指定的节点不必被包含。
Bessie 觉得这个策略的代价太昂贵了,考虑是否要改在 Mooloo 上看节目。为了帮助其决策,你应当告诉之 \(k\) 取遍 \(1\sim n\) 时看节目的最小总代价。
\(1\le n\le2\times10^5\)。
先考虑 \(n\le5000\) 时怎么做。
我们对每个 \(k\) 考虑怎么暴力求答案。
考虑 dp,\(f_{p,0/1}\) 表示在 \(p\) 的子树中,当前根节点是否被选入某个联通块的最小代价;一个联通块额外的 \(k\) 的代价将在其根节点向父亲做贡献时计算。
则
\]
\]
最后 \(\min\{f_{p,0},f_{p,1}+k\}\) 即为答案。
这样我们就得到一个 \(O(n)\) dp 的方法,对每个 \(k\) 暴力做一次,复杂度 \(O(n^2)\)。
(不过想在 USACO 的老爷机上过 \(5000\) 还是比较困难的,可能要加一些常数优化)
接下来考虑怎么优化。
一种想法是 slope trick,但是这个东西非常没有前途,常数也不小。
考虑另一种方法:优化暴力!
注意到,\(k=n\) 时答案仍不超过 \(2n\),我们的答案不会很大;同时,容易发现答案关于 \(k\) 具有凸性!
因此,我们的答案是一个关于 \(k\) 的凸壳!
我们考虑把凸壳上的每一段相同的线段一起处理,其为等差数列!
我们来证明一下凸壳上斜率不同的线段数目;显然斜率均为正整数。
假设斜率为 \(k\) 的线段有 \(a_k\) 条,则
\]
于是 \(a_k>0\) 的 \(k\) 是 \(O(\sqrt n)\) 级别的!
考虑直接二分该等差数列在何处结束,复杂度为 \(O(n\sqrt n\log n)\) 的。
这种东西显然过不去,考虑优化一下复杂度。
我们把二分换掉,改成先倍增,倍增到不可行后再二分。
这样,对斜率为 \(k\) 的线段,我们的查询轮数为 \(O(\log a_k)\) 的。
我们考虑分析一下 \(O(\sum_{a_k>0}\log a_k)\) 的级别。
我们把 \(\log a_k\) 描述为 \(\sum_t[2^t\le a_k]\),则即
\]
由于
\]
因此满足 \(a_k\ge w\) 的 \(k\) 是 \(O(\sqrt{n/w})\) 级别的。
因此
\]
因此只用做 \(O(\sqrt n)\) 轮查询。
总复杂度即为 \(O(n\sqrt n)\)。
这个东西如果实现的常数不够优秀,在 USACO 的老爷机上会 T!L!E!
因此考虑一个常数优化技巧:我们每轮 dp 进行 dfs 的常数太大了,且内存访问不连续,考虑预先进行一遍 dfs,把每个节点重标号一下,使得父亲标号小于当前标号,这样每次 dp 只用进行一轮循环即可了。
实践表明,这样的常数大约是原来的 \(1/6\) 左右,可以轻松通过。
核心代码:
std::vector<uint>Way[200005];
chr C[200005];
uint Dp[2][200005];
uint Dfn[200005],Fath[200005],cnt;
bol Op[200005];
voi dfs0(uint p,uint f){
Fath[Dfn[p]=cnt]=~f?Dfn[f]:-1u,Op[cnt++]=C[p]=='1';
for(auto s:Way[p])if(s!=f)dfs0(s,p);
}
std::map<uint,uint>M;
uint find(uint v){
if(M.count(v))return M[v];
for(uint i=0;i<cnt;i++)Dp[0][i]=0,Dp[1][i]=1;
for(uint i=cnt-1;i;i--){
if(Op[i]){
Dp[0][Fath[i]]+=Dp[1][i]+v;
Dp[1][Fath[i]]+=Dp[1][i];
}
else{
Dp[0][Fath[i]]+=std::min(Dp[0][i],Dp[1][i]+v);
Dp[1][Fath[i]]+=std::min(Dp[0][i],Dp[1][i]);
}
}
if(Op[0])Dp[0][0]=1e9;
return M[v]=std::min(Dp[0][0],Dp[1][0]+v);
}
uint Ans[200005];
int main()
{
#ifdef MYEE
freopen("QAQ.in","r",stdin);
// freopen("QAQ.out","w",stdout);
#endif
uint n;scanf("%u%s",&n,C);
for(uint i=1,u,v;i<n;i++)scanf("%u%u",&u,&v),Way[--u].push_back(--v),Way[v].push_back(u);
// printf("%u\n",find(1));
dfs0(0,-1);
for(uint l=1;l<=n;){
Ans[l]=find(l);
if(l==n)break;
Ans[l+1]=find(l+1);
if(Ans[l+1]==Ans[l]+1){
for(uint j=l+2;j<=n;j++)
Ans[j]=Ans[j-1]+1;
break;
}
uint len=2;
while(l+len<=n){
if(find(l+len)-Ans[l]!=(Ans[l+1]-Ans[l])*len)
break;
len<<=1;
}
uint p=l+(len>>1);
while(len>1){
uint mid=p+(len>>=1);
if(mid<=n&&find(mid)-Ans[l]==(Ans[l+1]-Ans[l])*(mid-l))p=mid;
}
for(uint j=l+2;j<=p;j++)Ans[j]=Ans[j-1]*2-Ans[j-2];
l=p+1;
}
for(uint i=1;i<=n;i++)printf("%u\n",Ans[i]);
return 0;
}
USACO2023Feb游记的更多相关文章
- 【20161203-20161208】清华集训2016滚粗记&&酱油记&&游记
先挖坑(这个blog怎么变成游记专用了--) 已更完 #include <cstdio> using namespace std; int main(){ puts("转载请注明 ...
- 【20160722-20160728】NOI2016滚粗记&&酱油记&&游记
先挖坑 #include <cstdio> using namespace std; int main(){ puts("转载请注明出处:http://www.cnblogs.c ...
- NOIp2016 游记
DAY -2 不要问我为什么现在就开了一篇博客. 本来想起个NOIp2016爆零记或者NOIp2016退役记之类的,但是感觉现在不能乱立flag了.所以就叫游记算了. 前几场模拟赛崩了一场又一场,RP ...
- NOIP2016游记
只是游记而已.流水账. Day0:忘了. Day1:看完T1,本以为T2一如既往很简单,结果看了半天完全没有思路.然后看了一眼T3,期望,NOIP什么时候要考期望了,于是接着看T2.一开始我推的限制条 ...
- CTSC2016&&APIO2016滚粗记&&酱油记&&游记<del>(持续更新)</del>
挖一波坑 #include <cstdio> using namespace std; int main(){ puts("转载请注明出处:http://www.cnblogs. ...
- 游记——noip2016
2016.11.18 (day 0) 呆在家. 悠闲地呆在家.. 明后天可能出现的错误: 1)没打freopen.打了ctime: 2)对拍程序忘记怎么写了...忘记随机化种子怎么写了: 3)不知道厕 ...
- 【NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2】游记
我第一次写游记,,,, 正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪 ...
- ZJOI2016二试+游记
...excited.... 一场打回原形爽哦. T1莫名爆到了10分,T2T3均没交,一个小时过后就没再拿任何分数,perfectly狗带了... 总之没有给自己充足的时间去敲暴力,ZJOI啊..拿 ...
- NOIP2015游记——一次开心又失望的旅行
啊,一年一度的NOIP终于是结束了 以前的大神都有写自己的感受 然而我居然给忘了!!!! 吓得我赶紧来写一份游记 Day.-INF--出发前一个星期 机智的我选择了停课 就是为了OIER这伟大而又光荣 ...
- CTSC2017 && APIO2017 && THUSC2017 游记
一去北京就是近20天,还是回来写写游记吧. 5.6 坐飞机到天津转动车到北京. 5.7 在天坛公园逛了一圈就去报到了. 下午试机,好像没发生什么. 5.8 CTSC一试 T1签到,开个桶打个标记就好了 ...
随机推荐
- 第四篇:前端之BOM与DOM
前端基础之BOM和DOM 前戏 到目前为止,我们已经学过了JavaScript的一些简单的语法.但是这些简单的语法,并没有和浏览器有任何交互. 也就是我们还不能制作一些我们经常看到的网页的一些交互 ...
- VC实例和VM实例的区别!!!
1.内置关系是什么 VueComponent.prototype.__proto__ === Vue.prototype 2.为什么要有这个关系 让组件实例对象可以访问到 Vue原型上的属性.方法.
- Javaweb后端学习笔记
C/S结构与B/S结构: 1.C/S(Client/Server)结构:适用于个人娱乐市场[QQ等] (1).优点:安全性高.且有效降低服务器压力: (2).不足:增加服务成本.更新较繁琐: 2.B/ ...
- Vue前后端分离实现登录的一个简单demo
1.建立一个Maven项目,并添加Spring相关依赖 2.编写Controller类相应的接口和配置类 LoginController类,编写接口的业务逻辑 package com.springbo ...
- C/S UDP通信实践踩坑记录与对于ICMP的进一步认识
背景 最近有个业务场景需要服务端(简称S)与客户端(简称C)设计一套基于UDP的通信协议--要求尽可能快的前提下可容忍一定丢包率,得以比较深入地学习和了解UDP通信和实践,在开发调试期间先后碰到了C端 ...
- [python] 基于paramiko库操作远程服务器
SSH(Secure Shell)是一种网络安全协议,能够使两台计算机安全地通信和共享数据.目前,SSH协议已在世界各地广泛使用,大多数设备都支持SSH功能.SSH的进一步说明见:深入了解SSH.SS ...
- [cocos2d-x]关于声音和音效
使用声音音效 CocosDesion实现了简单易用的SimpleAudioEngine类,为了使用它,我们只需引入他的头文件即可: #include "SimpleAudioEngine.h ...
- 从0开始学Java 第一期 开发前的准备
Java 学习(一) - 开发前的准备 前言 由于一些项目上的需要,我得学习一下 Java 这门语言(主要是想写Android),本人并非0基础,至少在上个学期学习了一门必修的程序设计(C语言),所以 ...
- post方法易错地方
<body> <h1>登录</h1> <input type="text" placeholder="请输入用户名" ...
- obj转换为gltf方法three.js一步一步来--性能优化超厉害的!!!!!超赞操作!!!Obj模型转Gltf模型并超强压缩!!!!!
1.准备好模型文件table.obj和table.mtl 2.下载obj2gltf 下载地址https://github.com/AnalyticalGraphicsInc/obj2gltf 解压至文 ...