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签到,开个桶打个标记就好了 ...
随机推荐
- selenium 之可视模式、静默模式、忽略证书不可用的设置
1.可视模式的设置(在前台工作) from selenium import webdriver import time url = "https://y.qq.com/n/ryqq/song ...
- 【深入浅出 Yarn 架构与实现】4-3 RM 管理 NodeManager
本篇继续对 RM 中管理 NodeManager 的部分进行深入的讲解.主要有三个部分:检查 NM 是否存活:管理 NM 的黑白名单:响应 NM RPC 请求. 一.简介 在 RM 的主从结构中,最主 ...
- Spark详解(04) - Spark项目开发环境搭建
类别 [随笔分类]Spark Spark详解(04) - Spark项目开发环境搭建 Spark Shell仅在测试和验证程序时使用的较多,在生产环境中,通常会在IDEA中编制程序,然后打成Ja ...
- Web初级——数组对象常用api
js数组常用api 连接函数:join("连接符") var array = [1,2,3,4,5] console.log(array.join("+")) ...
- VS2019注册码
Visual Studio 2019 Enterprise BF8Y8-GN2QH-T84XB-QVY3B-RC4DF Visual Studio 2019 Professional NYWVH-HT ...
- margin-left:auto的妙用
问题描述 如上图所示,我们需要将上述三个标签右对齐,就像上图是要达到的效果,一般采用的方法是对每个标签设置margin-left这样就需要设置三个值. 当文字变长或者变成英文的时候又需要重新设置,否则 ...
- P6327 区间加区间sin和 题解
P6327 区间加区间sin和 题解 题目描述 给出一个长度为 \(n\) 的整数序列 \(a_1,a_2,\ldots,a_n\),进行 \(m\) 次操作,操作分为两类. 操作 \(1\):给出 ...
- 影片自由,丝滑流畅,Docker容器基于WebDav协议通过Alist挂载(百度网盘/阿里云盘)Python3.10接入
使用过NAS(Network Attached Storage)的朋友都知道,它可以通过局域网将本地硬盘转换为局域网内的"网盘",简单理解就是搭建自己的"私有云" ...
- C#NPOI操作Excel,实现Excel数据导入导出(支持多个sheet)
首先在项目中引用NPOI,通过管理NuGet程序包,搜索NPOI,选择版本2.3.0(支持.NET Framework 4.0)根据自己项目选择适当版本. 1.NpoiExcelHelper.cs ...
- ‘mongo‘不是内部或外部命令,也不是可运行的程序或批处理文件
出现问题原因: MongoDB环境变量未配置 解决办法: 1)右击我的电脑-->属性,进入系统属性界面,点击如下图所示位置的[高级系统设置],在弹窗的[系统属性]的[高级]选项卡右下角点击[环境 ...