总传送门

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. 【uniapp 开发】字符串工具类 StringUtil

    替换字符串中的所有 "***" 子串 var str='Is this all there is'; var subStr=new RegExp('is','ig');//创建正则 ...

  2. java中设置准确的时间日期类的用法

    5.日期Date相关类: 题目1: 设置准确的时间(jdk1.1以后Date的setHours不被推荐了,所以要用Calendar设置时间) import java.util.*;public cla ...

  3. EMS邮箱数据库全局监控设置

    案例任务:监控TestDB01邮箱数据库的所有邮件,监控邮箱为用户"王淑江"的邮箱. 1.EMS全局监控设置 使用PowerShell完成操作:"王淑江"监控T ...

  4. BootstrapBlazor实战 Tree树形控件使用(2)

    继续上篇实战BootstrapBlazor树型控件Tree内容, 本篇主要讲解整合Freesql orm快速制作数据库后台维护页面 demo演示的是Sqlite驱动,FreeSql支持多种数据库,My ...

  5. Blazor组件自做五 : 使用JS隔离封装Google地图

    Blazor组件自做五: 使用JS隔离封装Google地图 运行截图 演示地址 正式开始 1. 谷歌地图API 谷歌开发文档 开始学习 Maps JavaScript API 的最简单方法是查看一个简 ...

  6. Linux上安装RePlace

    RePlAce: Advancing Solution Quality and Routability Validation in Global Placement 项目地址 https://gith ...

  7. XShell免费版的安装配置教程以及使用教程(超级详细)

    ​一. XShell的作用 XShell可以在Windows界面下来访问远端不同系统下的服务器,从而比较好的达到远程控制终端的目的.它支持 RLOGIN.SFTP.SERIAL.TELNET.SSH2 ...

  8. Dart语言基础

    文章目录 前言:dart语言简介 一.变量 1.1.类型推导 1.2.默认值 1.3.Final 和 const修饰符 二.内建类型 2.1.数据类型 2.2.集合的相关操作 三.函数 3.1.函数的 ...

  9. 前端之HTML标签

    一:HTML简介 1.超文本标记语言(Hypertext Markup Language, HTML)是一种用于创建网页的标记语言. 2.本质上是浏览器可识别的规则,我们按照规则写网页,浏览器根据规则 ...

  10. 【LeetCode】49. 字母异位词分组

    49. 字母异位词分组 知识点:字符串:哈希表 题目描述 给你一个字符串数组,请你将 字母异位词 组合在一起.可以按任意顺序返回结果列表. 字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源 ...