总传送门

T1. [USACO19JAN] Redistricting P

luogu P5202

  • 思路:

    这种每次选出段长有个上限\(k\)的常常是和单调队列有关。

    这里是单调队列优化dp

    不过一开始想不太清有什么单调性。

    发现每次的贡献为\(0/1\)

    因此如果\(i<j\)且\(dp_i<dp_j\)。\(i\)最多就和\(j\)一样贡献直接删去。

    如果\(dp_i=dp_j\)你需要考虑中间段的贡献,决定是否删。

    不过总之我们的目的是让优劣性单调递减(队首最优)

    如果段\([i+1..j]\)中\(cnt('H')>cnt('G')\)在\(i\)能取到时一定会比\(j\)优,所以保留\(i\)

    否则直接弹出\(i\)
  • code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
char s[N];
int dp[N],sh[N],sg[N];
int Q[N],hd=1,tl;
bool W(int l,int r) {
return (sg[r]-sg[l]>=sh[r]-sh[l]);
}
bool cmp(int x,int y) {return dp[x]==dp[y]?W(x,y):dp[x]>dp[y];}
int main() {
int n,k,l=1,r;
scanf("%d%d",&n,&k);
scanf("%s",s+1);
for(int i=1;i<=n;i++)sh[i]=sh[i-1]+(s[i]=='H'),sg[i]=sg[i-1]+(s[i]=='G');
dp[0]=0;Q[++tl]=0;
for(int i=1;i<=n;i++) {
while(hd<=tl&&Q[hd]+k<i) hd++;
dp[i]=dp[Q[hd]]+W(Q[hd],i);
while(hd<=tl&&cmp(Q[tl],i)) tl--;
Q[++tl]=i;
}
printf("%d\n",dp[n]);
return 0;
}

T2.[USACO20JAN] Cave Paintings P

luogu P6008

  • 思路:

    并查集

    最简单的思路就是从下往上灌水。

    如果某两个节点在\(i+1\)行不连通,在第\(i\)行联通。然后这两个节点在\(i……n\)时是独立的。(如果独立就相当于乘法原理,不独立就会合成同类一起作贡献)

    推广到带权并查集,合并两个联通块价值为它们乘积+1

    当然我考场上想复杂了。

    因为我一直往图论上套(思维僵化,犯了跟noionline一样的错误)。

    不过还是搞了很久,我把它缩点(如果两个点通过往下、左、右走能联通就合成一个点一起作贡献)

    可能因为我这种做法每行横向一段是手动缩点(而不是并查集),就跑的飞快。

    缩点后成为森林,树上dp。(别忘了记录完起点)

    具体缩点写法,(和正常写法很像)到能合并的时机时,加一个新点代表(合并后的点),连向所有每个都能到的下层的节点(下层的森林上的节点已经构造好了)。
  • code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5;
const int M=2e6+5;
const int mod=1e9+7;
int n,m,st[M],tp;
char s[N][N];
int tt[N],ct,Ll,Rl,fa[M],nxt[M],to[M],head[M],ecnt,nd,pos[N][N];
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
int gt_fa(int u) {return fa[u]==u?u:fa[u]=gt_fa(fa[u]);}
void Union(int u,int v) {fa[gt_fa(u)]=gt_fa(v);}
int rt[M];
void Build() {
bool bg=0;
for(int i=n;i;i--) {
// printf("!%d\n",i);
if(!bg) {
for(int j=1;j<=m;j++) {
if(s[i][j]=='.') {
++nd;fa[nd]=nd;pos[i][j]=nd;
while(s[i][j+1]=='.') {
j++;pos[i][j]=nd;
}
}
}
if(nd){Ll=1;Rl=nd;bg=1;}
continue;
}
for(int j=1;j<=m;j++) {
if(s[i][j]=='.') {
++nd;fa[nd]=nd;pos[i][j]=nd; //tmp node
if(pos[i+1][j])Union(nd,pos[i+1][j]);
while(s[i][j+1]=='.') {
j++;pos[i][j]=nd;if(pos[i+1][j])Union(nd,pos[i+1][j]);
}
}
}
// new node
int nwR=nd;
ct=0;
for(int j=1;j<=m;j++) {
if(s[i][j]=='#') continue;
int x=gt_fa(pos[i][j]);
if(!rt[x]) {rt[x]=++nd;tt[++ct]=x;fa[nd]=nd;}
pos[i][j]=rt[x];
}
for(int j=Ll;j<=Rl;j++) {
int x=gt_fa(j);
if(!rt[x]) st[++tp]=x; //beginning node
else add_edge(rt[x],j);
// printf("%d(%d) %d\n",rt[x],x,j);
}
Ll=nwR+1;Rl=nd;
while(ct) {rt[tt[ct]]=0;ct--;}
// printf("[%d,%d]\n",Ll,Rl);
}
}
ll dp[M];
void DP(int u) {
dp[u]=1;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
DP(v);
dp[u]=dp[u]*dp[v]%mod;
}
dp[u]++;
}
int main() {
// int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
Build();
ll ans=1;
while(tp) {
int i=st[tp--];
DP(i);ans=ans*dp[i]%mod;
}
printf("%lld",ans);
return 0;
}

T3.[USACO20JAN] Non-Decreasing Subsequences P

P6009

  • 题意:给每一个长为\(n\)的数列,每个数的上限是\(k\),\(Q\)次问\([l_i,r_i]\)内的最长不降子序列的方案数

  • 思路:

    动态dp

    线段树维护\(z[x][y]\)表示上升子序列的权值为从\(x\)到\(y\)的方案数。

    \(O(q\log_2{n}\ k^2)\)

    难写又慢,而且发现玄机(\(q\)是\(n\)的\(20\)倍)

    想要把询问\(q\)复杂度,转到\(n\)上。接下来用到:

    中轴分治(自己乱创的,类似cdq)

    即分治时每次处理跨过\(mid=(l+r)/2\)的区间

    然后这个区间的结果为\(mid\)左边的部分和右边的部分合并起来

    因此要预处理从\(mid\)往左的答案,从\(mid\)往右的答案

    如果处理和查询的每一步是\(O(1)\)的话,这个算法的复杂度是\(O(nlog_2n+q)\)

    这样就能把\(q\)的复杂度转化到\(n\)上呢。

    合并显然需要:

    \(G[i][y]\):(右半边)开始的值为\(y\),前缀\(i\)的总方案数

    \(F[i][y]\):(左半边)结束的值为\(y\),后缀\(i\)的总方案数

    这里只说右半边(\(G\))的做法,\(F\)同理

    方便\(G\)的转移,我们定义\(g[x][y]\)为上面的\(z[x][y]\)的意思

    然后每次新添加\(a_i\)时:只有\(g[y][a_i]\ \ (1<=y<=a_i)\)会受影响。

    要找前面的一个\(z<=a_i\)

    \(g[y][a_i]+=\sum\limits_{z=1}^{a_i}g[y][z]\)

    还要考虑\(a_i\)独立为一个子序列

    \(g[a_i][a_i]++\)

    这样\(O(k^2)\)处理了\(g\)

    \(G[i][y]=\sum\limits_{z=y}^kg[y][z]\) 知道开头\(y\)枚举结尾

    \(O(k^2)\)而且这个跑的挺满的,实际需要\(O(k)\)就可以啦。

    \(G[i-1][y]\)相比\(G[i][y]\)只有在\(y<=a_i\)时,\(g[y][a_i]\)的改变会影响\(G[i][y]\),此时加上\(g[y][a_i]\)的变化量即可。

    总复杂度\(nlognk^2+qk\)而且肯定是跑不满的。

    我翻了翻题解,题解搞了数据结构来优化到\(nklognlogk\)实际上因为数据结构卡死了上限还跑的比我慢很多。

  • code

戳我
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
const int M=N>>2;
const int K=21;
const int mod=1e9+7;
int a[N],b[N],q,n,k,rev[N],A[N],t1[N],t2[N];
//g[x][y]:(val)[x...y]; G[i][y]:(val)[y...],pre(pos) i
ll g[K][K],f[K][K],G[M][K],F[M][K],sG[M][K],ans[N];
struct query {int l,r;}Q[N];
void gt_gG(int l,int r) {
g[a[l]][a[l]]=G[l][a[l]]=1;
for(int i=l+1;i<=r;i++) {
int ai=a[i];
for(int y=1;y<=ai;y++) {
G[i][y]=G[i-1][y]-g[y][ai];
ll tmp=g[y][ai]+(y==ai);
for(int z=y;z<=ai;z++) tmp+=g[y][z];
g[y][ai]=(tmp%=mod);
G[i][y]=(G[i][y]+tmp)%mod;
}
for(int y=ai+1;y<=k;y++) {G[i][y]=G[i-1][y];}
}
}
void gt_fF(int l,int r) {
f[b[l]][b[l]]=F[l][b[l]]=1;
for(int i=l+1;i<=r;i++) {
int bi=b[i];
for(int y=bi;y<=k;y++) {
F[i][y]=F[i-1][y]-f[bi][y];
ll tmp=f[bi][y]+(y==bi);
for(int z=bi;z<=y;z++) tmp+=f[z][y];
f[bi][y]=(tmp%=mod);
F[i][y]=(F[i][y]+tmp)%mod;
}
for(int y=1;y<bi;y++)F[i][y]=F[i-1][y];
}
}
void Clear(int l,int r,int L,int R) {
for(int i=1;i<=k;i++)for(int j=1;j<=k;j++)g[i][j]=f[i][j]=0;
for(int i=l;i<=r;i++)for(int j=1;j<=k;j++)F[i][j]=0;
for(int i=L;i<=R;i++)for(int j=1;j<=k;j++)sG[i][j]=G[i][j]=0;
}
ll SG(int i,int j) {
if(sG[i][j])return sG[i][j];
for(int y=1;y<=j;y++) sG[i][j]=(sG[i][j]+G[i][y])%mod;
return sG[i][j];
}
void solve(int l,int r,int L,int R) {
int mid=(l+r)>>1;
gt_gG(mid+1,r);gt_fF(rev[mid],rev[l]);
int c1=L,c2=R;
for(int i=L;i<=R;i++) {
int x=A[i];
if(Q[x].l==Q[x].r) ans[x]=2;
else if(Q[x].l>mid) t2[c2--]=x;
else if(Q[x].r<=mid) t1[c1++]=x;
else {
int st=rev[Q[x].l],ed=Q[x].r;
ans[x]=1+SG(ed,k);
for(int yl=1;yl<=k;yl++) {
ans[x]=(ans[x]+F[st][yl]*(SG(ed,k)-SG(ed,yl-1)+1))%mod;
}
}
}
Clear(rev[mid],rev[l],mid+1,r);
if(c1!=L) {
for(int i=L;i<c1;i++) A[i]=t1[i];
solve(l,mid,L,c1-1);
}
if(c2!=R) {
for(int i=R;i>c2;i--) A[i]=t2[i];
solve(mid+1,r,c2+1,R);
}
} int main() {
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) {rev[i]=n-i+1;b[rev[i]]=a[i];}
// for(int i=1;i<=n;i++)printf("%d ",b[i]);puts("");
scanf("%d",&q);
for(int i=1;i<=q;i++) {scanf("%d%d",&Q[i].l,&Q[i].r);A[i]=i;}
solve(1,n,1,q);
for(int i=1;i<=q;i++) printf("%lld\n",(ans[i]+mod)%mod);
return 0;
}

清明欢乐赛(USACO选题)的更多相关文章

  1. contesthunter CH Round #64 - MFOI杯水题欢乐赛day1 solve

    http://www.contesthunter.org/contest/CH Round %2364 - MFOI杯水题欢乐赛 day1/Solve Solve CH Round #64 - MFO ...

  2. 2014.8.3情人节欢乐赛【Benny的农场】

    Benny的农场 (farm.pas/.c/.cpp) 时间限制:1s.空间限制:128MB 题目描述: Benny有一片农田需要灌溉.农田的形状为矩形,并被分为许多小块.每一块中都有一些水管.共有1 ...

  3. i春秋第二届春秋欢乐赛RSA256writeup

    i春秋第二届春秋欢乐赛writeup 下载之后进行解压 发现四个文件 0x01看到题目是RSA的  又看到public.key 所以直接用kali linux的openssl 0x02可以看到e就是E ...

  4. 2014-10-24 NOIP欢乐赛

    10-24NOIP欢乐赛 ——By 潘智力 题目名称 分火腿 无聊的会议 班服 时间限制 1s 1s 1s 内存限制 64MB 128MB 128MB 输入文件 hdogs.in meeting.in ...

  5. Comet OJ 夏季欢乐赛 篮球校赛

    Comet OJ 夏季欢乐赛 篮球校赛 题目传送门 题目描述 JWJU注重培养学生的"唱,跳,rap,篮球"能力.于是每年JWJU都会举办篮球校赛,来给同学们一个切磋篮球技术的平台 ...

  6. Comet OJ 夏季欢乐赛 Gree的心房

    Comet OJ 夏季欢乐赛 Gree的心房 题目传送门 题目描述 据说每一个走进Gree哥哥心房的小姑娘都没有能够再走出来-- 我们将Gree哥哥的心房抽象成一个n \times mn×m的地图,初 ...

  7. Comet OJ 夏季欢乐赛 分配学号

    Comet OJ 夏季欢乐赛 H 分配学号 题目传送门 题目描述 今天,是JWJU给同学们分配学号的一天!为了让大家尽可能的得到自己想要的学号,鸡尾酒让大家先从 [1,10^{18}][1,1018] ...

  8. Comet OJ 2019 夏季欢乐赛题解

    Comet OJ 2019 夏季欢乐赛题解 我是来骗访问量的 A 完全k叉树 \(n\)个点的完全k叉树的直径. 直接做 B 距离产生美 直接做 C 烤面包片 \(n!!!\mod p\) 显然\(n ...

  9. 【题解】Comet OJ 国庆欢乐赛 简要题解

    [题解]Comet OJ 国庆欢乐赛 简要题解 A 直接做 B 直接做,结论: \[ ans=\max([Max\ge \mathrm{sum}] Max,s[n]/2) \] C 考虑这样一个做法: ...

随机推荐

  1. ES6-11学习笔记--解构赋值

    解构赋值:按照一定模式,从数组和对象中提取值,对变量进行赋值.   数组解构 对象解构 字符串解构 应用场景     曾经的赋值噩梦,非解构赋值数组: let arr = [1, 2, 3]; let ...

  2. ubantu系统之 lunch时报错:no such file /....../.lunchrc

    no such file /....../.lunchrc 出现时: 使用 source build/envsetup.sh 执行完后 再用lunch

  3. CCF201712-2游戏

    问题描述 有n个小朋友围成一圈玩游戏,小朋友从1至n编号,2号小朋友坐在1号小朋友的顺时针方向,3号小朋友坐在2号小朋友的顺时针方向,--,1号小朋友坐在n号小朋友的顺时针方向. 游戏开始,从1号小朋 ...

  4. indexOf返回值问题

    String s = "aoood";System.out.println(s.indexOf(""));//返回0 System.out.println(s. ...

  5. CSS简单样式练习(七)

    运行效果: 源代码: 1 <!DOCTYPE html> 2 <html lang="zh"> 3 <head> 4 <meta char ...

  6. JavaEE期末复习知识点总结

    JavaEE期末复习知识点总结 Java企业应用开发环境 Maven的基础概念 Maven是一个项目管理工具,可以对 Java 项目进行构建.依赖管理 Maven仓库 Maven 仓库是项目中依赖的第 ...

  7. ORM中聚合函数、分组查询、Django开启事务、ORM中常用字段及参数、数据库查询优化

    聚合函数 名称 作用 Max() 最大值 Min() 最小值 Sum() 求和 Count() 计数 Avg() 平均值 关键字: aggregate 聚合查询通常都是配合分组一起使用的 关于数据库的 ...

  8. git的.gitignore文件内容

    **/pom.xml.versionsBackup **/target/ **/out/ *.class # Mobile Tools for Java (J2ME) .mtj.tmp/ .idea/ ...

  9. springboot jar包方式部署

    打好jar包后上传到 linux 执行命令 java -jar /root/vhr-web-0.0.1-SNAPSHOT.jar > /root/log.txt & 1.java -ja ...

  10. 内存之旅——如何提升CMA利用率?

    ​(以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点)​ 宋远征 李佳伟 OpenAtom OpenHarmony(以下简称"OpenHarmony") ...