救生机甲终于找到,无意义防守一把
2026-10-11 23:19:57
发布于:广东
JOI 合集。
这个,大概是按,切题顺序
P14420 [JOISC 2014] 历史的研究 / Historical Research
这期讲过。
回滚莫队板子,莫队时枚举每个块,块内打暴力,块外以左端点为标准,从块右端点往左端点每次做 的暴力转移,右端点一直移动即可。每个询问做完回滚状态数组规避直接删除元素。然后直接做。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=200005;
int n,m,tot,B,T,b[N],cnt[N],a[N],ans[N],c[N],res,L,R,tmp,cur,bl[N],br[N],be[N];
struct qry{
int l,r,id;
bool operator<(const qry &y)const{return (be[l]^be[y.l])?l<y.l:r<y.r;}
}q[N];
int bf(int l,int r){
int ans=0;
for(int i=l;i<=r;++i)++c[a[i]];
for(int i=l;i<=r;++i)ans=max(ans,c[a[i]]*b[a[i]]);
for(int i=l;i<=r;++i)--c[a[i]];
return ans;
}
int read(){
int x=0,f=1,ch=getchar_unlocked();
for(;!isdigit(ch);ch=getchar_unlocked())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar_unlocked())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
void write(int x){
if(x>=10)write(x/10);
putchar_unlocked(x%10+'0');
}
signed main(){
n=read(),m=read(),T=sqrt(n),B=n/T;
for(int i=1;i<=n;++i)a[i]=b[i]=read();
for(int i=1;i<=m;++i)q[i]={read(),read(),i};
sort(b+1,b+n+1),tot=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;++i)a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
for(int i=1;i<=B;++i)bl[i]=br[i-1]+1,br[i]=bl[i]+T-1;
if(br[B]<n)++B,bl[B]=br[B-1]+1,br[B]=n;
for(int i=1;i<=n;++i)be[i]=(i-1)/T+1;
sort(q+1,q+m+1);
for(int i=1,idx=1;i<=B;++i){
memset(cnt,0,sizeof cnt);
R=br[i];
tmp=0;
while(be[q[idx].l]==i){
L=br[i]+1;
if(q[idx].r-q[idx].l<=T){
ans[q[idx].id]=bf(q[idx].l,q[idx].r);
++idx;
continue;
}
while(q[idx].r>R)++R,tmp=max(tmp,(++cnt[a[R]])*b[a[R]]);
cur=tmp;
while(q[idx].l<L)--L,tmp=max(tmp,(++cnt[a[L]])*b[a[L]]);
ans[q[idx].id]=tmp;
tmp=cur;
while(L<=br[i])--cnt[a[L++]];
++idx;
}
}
for(int i=1;i<=m;++i)write(ans[i]),putchar('\n');
return 0;
}
P9525 [JOIST 2022] 团队竞技 / Team Contest
!?简单贪?!
考虑堆对每个能力维护最大值,如果没有重复则是最优,否则当前重复的元素是不可能和后面任何元素继续匹配的。然后发现做完了,不合法情况把重复的元素在三个堆里面都删除就行了。
#include<bits/stdc++.h>
using namespace std;
int n,a[150005],b[150005],c[150005];
priority_queue<pair<int,int>>A,B,C;
bool vis[150005];
#define X A.top().second
#define Y B.top().second
#define Z C.top().second
inline int read(){
int x=0,f=1,ch=getchar_unlocked();
for(;!isdigit(ch);ch=getchar_unlocked())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar_unlocked())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
inline void write(int x){
x<0?x=-x,putchar_unlocked('-'):0;static short st[10],top(0);
do st[++top]=x%10,x/=10;while(x);
while(top)putchar_unlocked(st[top--]|48);
}
int main(){
n=read();
for(int i=1;i<=n;++i)A.push({a[i]=read(),i}),B.push({b[i]=read(),i}),C.push({c[i]=read(),i});
for(int i=1;i<=n;++i){
if(b[X]==b[Y]||c[X]==c[Z])vis[X]=1;
if(c[Y]==c[Z]||a[Y]==a[X])vis[Y]=1;
if(a[Z]==a[X]||b[Z]==b[Y])vis[Z]=1;
if(!vis[X]&&!vis[Y]&&!vis[Z]){write(a[X]+b[Y]+c[Z]);return 0;}
while(!A.empty()&&vis[A.top().second])A.pop();
while(!B.empty()&&vis[B.top().second])B.pop();
while(!C.empty()&&vis[C.top().second])C.pop();
}
write(-1);
return 0;
}
P9599 [JOI Open 2018] 木琴 / Xylophone
这期讲过。
简单题,查询每个长度为 和 的区间,前者显然能帮我们判断相邻元素差,后者能通过极差和排列性质让我们判断相邻的二元组是否单调,可以通过第一个二元组符号判断整个排列真实数值,考虑第一个二元组分别是上升和下降的情况哪个合法即可。
int query(int s, int t);
void answer(int i, int a);
int a[5005],s[5005],p,lst,xyl;
void solve(int n){
int mini=1,maxi=1;
s[2]=lst=query(1,2),xyl=1;
if(s[mini]>s[2])mini=2;
if(s[maxi]<s[2])maxi=2;
for(int i=2;i<n;++i){
p=query(i,i+1);
if((p+lst)^query(i-1,i+1))xyl=-xyl;
s[i+1]=s[i]+xyl*p;
if(s[mini]>s[i+1])mini=i+1;
if(s[maxi]<s[i+1])maxi=i+1;
lst=p;
}
if(mini<maxi)for(int i=1;i<=n;++i)answer(i,s[i]+1-s[mini]);
else for(int i=1;i<=n;++i)answer(i,s[maxi]-s[i]+1);
}
P9196 [JOI Open 2016] 销售基因链 / Selling RNA Strands
板题。很有意思。
多个字符串对多个字符串进行贡献,每个有序对只有一次贡献,考虑能不能 ACAM。如果我们用 ACAM 一定是要转化的,要求一边能匹配前缀一边能匹配后缀,那我们把基因库里的字符串自己拼特殊字符拼自己,就有了一个后缀接特殊字符接前缀的性质,对查询串后缀拼特殊字符拼前缀即可。然后就是多模匹配,不跳 fail,最后做个前缀和,直接输出每个串末尾节点的答案。
#include<bits/stdc++.h>
using namespace std;
int n,m,cnt,q[4200005],p[100005],l,r,ae[130];
string f,g,w[200005];
struct xyl{int fail,num,ch[5];}t[4200005];
#define v (t[u].ch[i])
void trie(int s){
int u=0,l=g.length();
for(int i=0;i<l;u=t[u].ch[ae[g[i]]],++i)if(!t[u].ch[ae[g[i]]])t[u].ch[ae[g[i]]]=++cnt;
l=f.length();
for(int i=0;i<l;u=t[u].ch[ae[f[i]]],++i)if(!t[u].ch[ae[f[i]]])t[u].ch[ae[f[i]]]=++cnt;
p[s]=u;
}
void build(){
for(int i=0;i<5;++i)
if(t[0].ch[i])t[t[0].ch[i]].fail=0,q[++r]=t[0].ch[i];
while(l<r){
int u=q[++l];
for(int i=0;i<5;++i)
if(v)t[v].fail=t[t[u].fail].ch[i],q[++r]=v;
else t[u].ch[i]=t[t[u].fail].ch[i];
}
}
void mccz(int id){
int u=0,l=w[id].length();
for(int i=0;i<l;++i)u=t[u].ch[ae[w[id][i]]],t[u].num++;
}
inline void write(int x){
x<0?x=-x,putchar_unlocked('-'):0;static short st[10],top(0);
do st[++top]=x%10,x/=10;while(x);
while(top)putchar_unlocked(st[top--]|48);
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0);
ae['A']=0,ae['G']=1,ae['U']=2,ae['C']=3,ae['B']=4;
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>f,w[i]=f+"B"+f;
for(int i=1;i<=m;++i)cin>>f>>g,g+='B',trie(i);
build();
for(int i=1;i<=n;++i)mccz(i);
for(int i=r;i;--i)t[t[q[i]].fail].num+=t[q[i]].num;
for(int i=1;i<=m;++i)write(t[p[i]].num),putchar_unlocked('\n');
return 0;
}
P6116 [JOI 2019 Final] 有趣的家庭菜园 3 / Growing Vegetables is Fun 3
太牛了这题。我还得练。
小清新啊。交换两个同颜色的花盆没有必要,因此最后同一颜色的花盆内部顺序肯定是不变的。套路地设 表示考虑前 个花盆, 个 颜色, 个 颜色,第 位为 的次数。下面的 dp 思路都是考虑从上一个不同颜色的状态转移。如果以 颜色为例,若前 个花盆中已经存在了 个 颜色和 个 颜色,我们直接让下一个 前移把多余的其他颜色挤开回去即可;否则如果目前要转移的原状态区间内某种颜色比原来更多了,肯定是之前的操作使这部分元素前移了,然后恰好前移多少个异类元素我们下一个要放到末尾的颜色就会后移多少(同一种颜色不会把它撞到后面去),分别统计另外两种颜色把它往后撞了多少(前缀和一下就可以快速查询区间里原来有多少个某种颜色),得到新位置,把它从新位置直接移到第 位我们这个转移就是对的了。复杂度 。
#include<bits/stdc++.h>
using namespace std;
#define mx(x,y) ((x>y)?(x):(y))
#define mn(x,y) ((x<y)?(x):(y))
#define gc getchar_unlocked
#define pc putchar_unlocked
typedef long long ll;
inline int read(){
int x=0,f=1,ch=gc();
for(;!isdigit(ch);ch=gc())if(ch=='-')f=-1;
for(;isdigit(ch);ch=gc())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
inline void write(int x){
x<0?x=-x,pc('-'):0;static short st[11],top(0);
do st[++top]=x%10,x/=10;while(x);
while(top)pc(st[top--]|48);
}
int n,f[2][405][405][3],a[405][3],s[405][3],c1,c2,c3,ch;
signed main(){
n=read();
for(int i=1;i<=n;++i){
ch=gc();
s[i][0]=s[i-1][0],s[i][1]=s[i-1][1],s[i][2]=s[i-1][2];
if(ch=='R')a[++c1][0]=i,s[i][0]++;
if(ch=='G')a[++c2][1]=i,s[i][1]++;
if(ch=='Y')a[++c3][2]=i,s[i][2]++;
}
if(mx(c1,mx(c2,c3))>(n+1)/2){write(-1);return 0;}
memset(f,0x3f,sizeof f);
f[1][1][0][0]=a[1][0]-1,f[1][0][1][1]=a[1][1]-1,f[1][0][0][2]=a[1][2]-1;
for(int i=2;i<=n;++i)
for(int j=0;j<=c1;++j)
for(int k=0,l=i-j-k;k<=c2;++k,l=i-j-k){
if(l>c3)continue;
if(j)f[i%2][j][k][0]=mn(f[(i-1)%2][j-1][k][1],f[(i-1)%2][j-1][k][2])+a[j][0]+mx(0,k-s[a[j][0]][1])+mx(0,l-s[a[j][0]][2])-i;
if(k)f[i%2][j][k][1]=mn(f[(i-1)%2][j][k-1][0],f[(i-1)%2][j][k-1][2])+a[k][1]+mx(0,j-s[a[k][1]][0])+mx(0,l-s[a[k][1]][2])-i;
if(l)f[i%2][j][k][2]=mn(f[(i-1)%2][j][k][0],f[(i-1)%2][j][k][1])+a[l][2]+mx(0,j-s[a[l][2]][0])+mx(0,k-s[a[l][2]][1])-i;
}
write(mn(mn(f[n%2][c1][c2][0],f[n%2][c1][c2][1]),f[n%2][c1][c2][2]));
return 0;
}
P6879 [JOI 2020 Final] 集邮比赛 3 / Collecting Stamps 3
还是太弱了,开题时有几个点没有想到。
很明显是个区间 dp,首先肯定需要做个化环为链,起点直接插中间,初始化可以确保状态从起点转移才是优的。dp 当然是哪个维度小我们就设哪个,时间这东西很不好转移,直接把答案(收集了多少个)拿过来当一个维度,收集时间变成 dp 维护的答案。另外两个维度自然是区间。一般区间 dp 转移需要 的复杂度,我们的复杂度就是 了,咋办?但是我们的移动确保了被收集的物品区间一定是逐步向外扩展的,dp 状态每次只扩展出一个,而不是像一般区间 dp 可以直接将两个被分割开的区间直接并一起,那么对区间 ,只用考虑 和 两个地方的状态即可,这其实是经典 trick。然后很快啊!注意到诶你走到的位置不是不在最左边就在最右边吗,那分讨两种状态不就把问题转化为 A+B Problem 了?转移时我们也可以轻松考虑爆炸的情况。显然地,最终答案是所有长度合法且所需时间不为初始值 inf 的区间中收集数这一状态最大的值。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=202;
#define mx(x,y) ((x>y)?(x):(y))
#define mn(x,y) ((x<y)?(x):(y))
#define gc getchar_unlocked
#define pc putchar_unlocked
inline int read(){
int x=0,f=1,ch=gc();
for(;!isdigit(ch);ch=gc())if(ch=='-')f=-1;
for(;isdigit(ch);ch=gc())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
inline void write(long long x){
x<0?x=-x,pc('-'):0;static short st[19],top(0);
do st[++top]=x%10,x/=10;while(x);
while(top)pc(st[top--]|48);
}
int n,L,ans,a[N],t[N],f[N][N][N][2];
signed main(){
n=read(),L=read();
for(int i=1;i<=n;i++)a[i]=read();
a[n+1]=L;
for(int i=1;i<=n;i++)t[i]=read();
memset(f,0x3f,sizeof f);
f[0][0][0][0]=f[0][0][0][1]=0;
for(int l=0;l<=n;l++)
for(int r=0;r<=n;r++){
if(l+r>=n)break;
for(int k=0;k<=n;k++){
int tmp=f[l][r][k][0];
if(tmp<=1e13){
f[l+1][r][k+(tmp+a[n-l+1]-a[n-l]<=t[n-l])][0]=mn(f[l+1][r][k+(tmp+a[n-l+1]-a[n-l]<=t[n-l])][0],tmp+a[n-l+1]-a[n-l]);
f[l][r+1][k+(tmp+L-a[n-l+1]+a[r+1]<=t[r+1])][1]=mn(f[l][r+1][k+(tmp+L-a[n-l+1]+a[r+1]<=t[r+1])][1],tmp+L-a[n-l+1]+a[r+1]);
}
tmp=f[l][r][k][1];
if(tmp<=1e13){
f[l+1][r][k+(tmp+L-a[n-l]+a[r]<=t[n-l])][0]=mn(f[l+1][r][k+(tmp+L-a[n-l]+a[r]<=t[n-l])][0],tmp+L-a[n-l]+a[r]);
f[l][r+1][k+(tmp+a[r+1]-a[r]<=t[r+1])][1]=mn(f[l][r+1][k+(tmp+a[r+1]-a[r]<=t[r+1])][1],tmp+a[r+1]-a[r]);
}
}
}
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=n;k++)if(mn(f[i][j][k][0],f[i][j][k][1])<1e15)ans=mx(ans,k);
write(ans);
return 0;
}
P7405 [JOI 2021 Final] 雪球 / Snowball
简单题,为啥没做出来,我是区。哦哦哦因为做过 USACO 某个神秘二维数点题导致以为这题也是 ds 数点。对啊,二分题怎么能评青呢,对啊。
哦哦哦每个点移动过程一模一样,相邻的球到底是怎样妨碍我的!哦哦哦如果我有一次占领了一个点,别人从来没到过,那这段区间肯定全是我的了!发现这是可以二分的,某个时间以前两球之间没被榨干,自此以后这段间隙里的雪一滴都不剩了,统一计算雪球每个时间点曾最左移动到哪和最右移动到哪就能找到这个冲突点,我移动的轨迹碰到别人轨迹了。然后对每个相邻的雪球都玩这么一套二分,过了过了。
#include<bits/stdc++.h>
using namespace std;
#define mx(x,y) ((x>y)?(x):(y))
#define mn(x,y) ((x<y)?(x):(y))
#define gc getchar_unlocked
#define pc putchar_unlocked
typedef long long ll;
inline ll read(){
ll x=0,f=1,ch=gc();
for(;!isdigit(ch);ch=gc())if(ch=='-')f=-1;
for(;isdigit(ch);ch=gc())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
inline void write(ll x){
x<0?x=-x,pc('-'):0;static short st[19],top(0);
do st[++top]=x%10,x/=10;while(x);
while(top)pc(st[top--]|48);
}
int n,m;
ll L[200005],R[200005],a[200005],ans[200005],sum;
inline void ae(int x,ll len){
if(L[m]+R[m]<=len){ans[x]+=R[m],ans[x+1]+=L[m];return;}
int l=1,r=m,k=m,mid;
while(l<=r){
mid=(l+r)>>1;
if(L[mid]+R[mid]>len)k=mid,r=mid-1;
else l=mid+1;
}
if(L[k]==L[k-1])ans[x+1]+=L[k],ans[x]+=len-L[k];
else ans[x]+=R[k],ans[x+1]+=len-R[k];
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<=m;++i)sum+=read(),L[i]=mx(L[i-1],-sum),R[i]=mx(R[i-1],sum);
ans[1]+=L[m],ans[n]+=R[m];
for(int i=1;i<n;++i)ae(i,a[i+1]-a[i]);
for(int i=1;i<=n;++i)write(ans[i]),pc('\n');
return 0;
}
P8162 [JOI 2022 Final] 让我们赢得选举 / Let's Win the Election
我们冲刺群的人已经就本次模拟赛题目难度达成一致。其原因是这模拟赛咋全是洛谷原题哦。拜谢 T3,沉迷水群水洛谷水按错宫导致我完全开摆了导致我这题打了点部分分连 dp 都没搓而且这场的数据是啥啊还有人假贪心过这题来着唉牛了牛了。
发现我们的演讲肯定是目前所有支持者一起进行同一个演讲,这样一定是比分头演讲更优的。并且每个州只有三种情况,不演讲、获得选票和既获得选票又获得支持者三种情况,这样就在多维 dp 的实现范围内了,看数据范围也能验证我们对做法的猜测。不演讲、获得选票和既获得选票又获得支持者三种情况分别为 、、 类州,dp 状态为 表示考虑 个州,获得 个选票和 个 类的情况。
但是还要先钦定一种 dp 顺序,那我们尝试确定一种最优的顺序。发现 类州唯一的作用是加选票,无论顺序怎么放只要演讲过就有贡献,而 类演讲完可以提升后面的演讲效率且提升幅度是一样的。那肯定要把所有 放前面,所有 放后面。!?BA?!然后对于 类,反正贡献一样,越早把支持者拿下后面就越快,按 升序排序显然是最优的。所以我们 dp 前先对序列按 升序排序。
然后分三种情况,当前州不选,答案就直接是 ;选为 类,前面吃过了 次加成,答案就加上 ;选为 类,我们的顺序肯定放在所有 类之后,但我们现在还不清楚最后要选多少个 类,只能每次钦定一个数 ,对 dp 时这个转移就为 。这样 dp 是 的。
怎么优化呢?其实枚举 的做法还是有前途的,因为 类转移和 类转移没关系,我们可以只转移 再想着怎么去算 。看起来,前面的序列既有 又有 ,这样很难搞。但是实际上序列并不是混合的,最后一个 类州之前肯定是没有 类的,因为这样不如把二者交换一个,原来那个 变成 之后 更小了,是更优的。所以,如果我们 dp 只考虑 ,前面的序列就只有 和 了,把原来那个 省略,定为 即可。dp 后还有若干个 州,显然应当取 最小的一组,这里排序后就能直接贪心。我们做完 的 dp 后再逐个加上后面的贡献更新答案就行了。
#include<bits/stdc++.h>
using namespace std;
#define mx(x,y) ((x>y)?(x):(y))
#define mn(x,y) ((x<y)?(x):(y))
#define gc getchar_unlocked
#define pc putchar_unlocked
typedef long long ll;
inline int read(){
int x=0,f=1,ch=gc();
for(;!isdigit(ch);ch=gc())if(ch=='-')f=-1;
for(;isdigit(ch);ch=gc())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
inline void write(ll x){
x<0?x=-x,pc('-'):0;static short st[20],top(0);
do st[++top]=x%10,x/=10;while(x);
while(top)pc(st[top--]|48);
}
int n,k;
struct xyl{int a,b;}a[505],t[505];
inline bool cmp(xyl x,xyl y){return x.a<y.a;}
inline bool cmp2(xyl x,xyl y){return x.b<y.b;}
double f[505][505],g[505][505],ans=1e8;
signed main(){
n=read(),k=read();
for(int i=1;i<=n;++i)a[i]={read(),read()},a[i].b=(~a[i].b)?a[i].b:1e9;
sort(a+1,a+n+1,cmp2);
for(int i=1;i<=n;++i)t[i]=a[i];
for(int i=n;i;--i){
sort(a+i,a+n+1,cmp);
for(int j=i;j<=n;++j)g[i][j-i+1]=g[i][j-i]+a[j].a;
}
for(int l=0;l<k;++l){
for(int i=0;i<=k;++i)for(int j=0;j<=l;++j)f[i][j]=2e8;
f[0][0]=0,ans=min(ans,f[0][l]+g[1][k]/(l+1));
for(int i=1;i<=k;++i){
f[i][0]=f[i-1][0]+(double)t[i].a/(l+1);
for(int j=1;j<=l;++j)f[i][j]=min(f[i-1][j-1]+(double)t[i].b/j,f[i-1][j]+(double)t[i].a/(l+1));
ans=min(ans,f[i][l]+g[i+1][k-i]/(l+1));
}
}
printf("%.15f",ans);
return 0;
}
这里空空如也

















有帮助,赞一个