20250602比赛总结
T1 皮胚 (match)
https://www.gxyzoj.com/d/hzoj/p/4699
简单 dp,设 \(dp_{i,j}\) 表示原串第 \(i\) 位,匹配串第 \(j\) 位有没有解
如果当前是 . 或字母,就是一对一,如果是 *,那么看等价的位置是否有转移
此时可以刷表,因为只会填 \(n^2\) 个位置,时间复杂度 \(O(n^2)\)
但是注意最后可能是 *,要注意跑没跑到,不然随机挂
T2 核冰 (merge)
https://www.gxyzoj.com/d/hzoj/p/4700
线段树就是一个神经的东西
40分:按题意模拟即可
贪心显然,就是能合则合,但是原有的至少留 1 个
显然合并前和合并后的奇偶性是一致的,将合并前的数量组成的数组记为 cnt
观察发现,如果将每一次修改看作一次加入和一次删除
加入就是找到第一个合并后的 1 或 0 去增加,中间的 2 全部 -1,删除就是找到第一个 2,或者没有向前合并的 1 减少,中间的 1 全部 +1
把这个放到 cnt 数组上,加入就是找奇数或 0,删除是找偶数或 1
但这样正常的线段树显然不行,可以使用不严格的线段树二分
这里记录两个标记 t1,t0,即是否全为 1/0,然后记录段最小值,看能否段内更新
这样如果左边全不满足,就要再递归右边,所以时间不是严格的 log
点击查看代码
#include<cstdio>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
using namespace std;
const int N=5e5;
int n,m,a[1000005],cnt[1000005],ans;
struct seg_tr{
int l,r,t1,t0,mn,lazy;
}tr[4000005];
void pushup(int id)
{
tr[id].t0=tr[lid].t0&tr[rid].t0;
tr[id].t1=tr[lid].t1&tr[rid].t1;
tr[id].mn=min(tr[lid].mn,tr[rid].mn);
}
void build(int id,int l,int r)
{
tr[id].l=l,tr[id].r=r;
if(l==r)
{
tr[id].mn=cnt[l];
if(cnt[l]%2==0) tr[id].t0=1;
else tr[id].t1=1;
return;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
void update(int id,int x)
{
tr[id].mn+=x;
tr[id].lazy+=x;
if(x%2) swap(tr[id].t0,tr[id].t1);
}
void pushdown(int id)
{
if(tr[id].lazy)
{
update(lid,tr[id].lazy);
update(rid,tr[id].lazy);
tr[id].lazy=0;
}
}
int add(int id,int x)
{
if(tr[id].l>=x)
{
if(tr[id].t0&&tr[id].mn)
{
update(id,1);
return tr[id].r;
}
if(tr[id].l==tr[id].r)
{
if(!tr[id].mn)
{
tr[id].mn++,tr[id].t0=0,tr[id].t1=1;
ans++;
}
else update(id,1);
return tr[id].r-1;
}
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)>>1,tmp=0;
if(x<=mid) tmp=add(lid,x);
if(x>mid||tmp==mid) tmp=add(rid,x);
pushup(id);
return tmp;
}
int del(int id,int x)
{
if(tr[id].l>=x)
{
if(tr[id].t1&&tr[id].mn>1)
{
update(id,-1);
return tr[id].r;
}
if(tr[id].l==tr[id].r)
{
if(tr[id].mn==1)
{
tr[id].mn=0,tr[id].t0=1,tr[id].t1=0;
ans--;
}
else update(id,-1);
return tr[id].r-1;
}
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)>>1,tmp=0;
if(x<=mid) tmp=del(lid,x);
if(x>mid||tmp==mid) tmp=del(rid,x);
pushup(id);
return tmp;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
cnt[a[i]]++;
}
for(int i=1;i<=N;i++)
{
cnt[i]=(cnt[i-1]-1)/2+cnt[i];
if(cnt[i]>0) ans++;
}
build(1,1,N);
while(m--)
{
int opt,x,y;
scanf("%d",&opt);
if(opt==1)
{
scanf("%d%d",&x,&y);
int tmp=del(1,a[x]);
a[x]=y;
tmp=add(1,a[x]);
}
else printf("%d\n",ans);
}
return 0;
}
T3 方珍 (mex)
https://www.gxyzoj.com/d/hzoj/p/4701
先考虑如何找第 k 大的,如果要统计 mex 在某范围内的值,显然只需要前面的值都存在即可
所以对于单行,可以二分+双指针
但是多行的情况下,考虑什么时候 ans 会变化,可以先按 w 从大到小排序,那么当 \(mex>ans-w_i\) 时才更新
所以判断过去即可,因为这个东西至多增加 n 次,时间复杂度 \(O(n^2)\)
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
int n,k[10004],w[10005],m[10004],a[10004],id[10004];
int cnt[10005];
ull seed[10004];
ull _rand(int x)
{
seed[x]^=seed[x]<<13;
seed[x]^=seed[x]>>7;
seed[x]^=seed[x]<<17;
return seed[x];
}
bool cmp(int x,int y)
{
return w[x]>w[y];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%llu",&k[i],&w[i],&m[i],&seed[i]);
id[i]=i;
}
int tmp=(n+1)*n/2;
sort(id+1,id+n+1,cmp);
int v=1,ans=0;
for(int x=1;x<=n;x++)
{
// printf("%d %d\n",x,id[x]);
for(int i=1;i<=n;i++)
{
a[i]=_rand(id[x])%m[id[x]];
// printf("%d ",a[i]);
}
// printf("a\n");
v=max(v,ans-w[id[x]]);
for(;v<=m[id[x]]+1;v++)
{
int i=1,j=1,p=0,sum=0;
for(;i<=n+1;i++)
{
if(p==v) break;
if(a[i]<v) cnt[a[i]]++;
while(cnt[p]) p++;
}
if(p!=v) break;
while(j<i&&(a[j]>=v||cnt[a[j]]>1))
{
if(a[j]<v) cnt[a[j]]--;
j++;
}
sum=j;
// printf("%d %d\n",i,j);
for(;i<=n;i++)
{
if(a[i]<v) cnt[a[i]]++;
while(j<=i&&(a[j]>=v||cnt[a[j]]>1))
{
if(a[j]<v) cnt[a[j]]--;
j++;
}
sum+=j;
}
for(i=0;i<=n;i++) cnt[i]=0;
// printf("%d %d %d %d\n",id[x],x,v,sum);
if(sum<tmp-k[id[x]]+1) break;
}
ans=max(ans,v-1+w[id[x]]);
v--;
}
printf("%d",ans);
return 0;
}
T4 术劣 (sequence)
https://www.gxyzoj.com/d/hzoj/p/4701
有一个性质 满足 \(gcd(a_2-a_1,\dots,a_n-a_{n-1})*(n-1)=max\{a_i\}-min\{a_i\}\) 的序列排序后是等差数列
变形并替换一下得到 \(d*r=l*d+max\{a_i\}-min\{a_i\}\)
对于 max 和 min 直接单调栈在线段树上更新即可,关键是 d
因为这个东西满足交换律,所以可以把到 r-1 gcd 相同的直接合起来,如果发生变化,单点修改
因为 gcd 至多变 logn 次,时间复杂度 \(O(nlog^2n)\)
因为右边的值最小就是 \(d*r\),所以线段树统计最小值和最小值个数即可
点击查看代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
#define ll long long
using namespace std;
int gcd(int a,int b)
{
if(b==0) return a;
return gcd(b,a%b);
}
int n,a[200005],f[200005],l[200005],st1[200005],st2[2000005],t1,t2;
ll b[200005];
int find(int x)
{
if(f[x]!=x) f[x]=find(f[x]);
return f[x];
}
struct seg_tr{
int l,r,cnt;
ll mn,lazy;
}tr[800005];
void build(int id,int l,int r)
{
tr[id].l=l,tr[id].r=r,tr[id].cnt=r-l+1;
if(l==r)
{
return;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
}
void pushdown(int id)
{
if(tr[id].lazy)
{
tr[lid].lazy+=tr[id].lazy;
tr[lid].mn+=tr[id].lazy;
tr[rid].lazy+=tr[id].lazy;
tr[rid].mn+=tr[id].lazy;
tr[id].lazy=0;
}
}
void pushup(int id)
{
tr[id].mn=min(tr[lid].mn,tr[rid].mn);
tr[id].cnt=0;
if(tr[lid].mn==tr[id].mn) tr[id].cnt=tr[lid].cnt;
if(tr[rid].mn==tr[id].mn) tr[id].cnt+=tr[rid].cnt;
}
void update(int id,int l,int r,ll x)
{
if(tr[id].l==l&&tr[id].r==r)
{
tr[id].mn+=x,tr[id].lazy+=x;
return;
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)>>1;
if(r<=mid) update(lid,l,r,x);
else if(l>mid) update(rid,l,r,x);
else update(lid,l,mid,x),update(rid,mid+1,r,x);
pushup(id);
}
ll query(int id,int l,int r,ll k)
{
if(tr[id].l==l&&tr[id].r==r)
{
if(tr[id].mn==k) return tr[id].cnt;
return 0;
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)>>1;
if(r<=mid) return query(lid,l,r,k);
else if(l>mid) return query(rid,l,r,k);
else return query(lid,l,mid,k)+query(rid,mid+1,r,k);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
f[i]=l[i]=i;
}
build(1,1,n);
ll ans=1;
for(int r=1;r<=n;r++)
{
while(t1&&a[st1[t1]]>=a[r])
{
update(1,st1[t1-1]+1,st1[t1],a[st1[t1]]-a[r]);
t1--;
}
while(t2&&a[st2[t2]]<=a[r])
{
update(1,st2[t2-1]+1,st2[t2],a[r]-a[st2[t2]]);
t2--;
}
st1[++t1]=r,st2[++t2]=r;
if(r==1)
{
printf("1 ");
continue;
}
int now=abs(a[r]-a[r-1]),lst=r-1;
b[r-1]=now;
update(1,r-1,r-1,1ll*now*(r-1));
for(int i=r-2;i>0;i--)
{
i=find(i);
now=gcd(now,b[i]);
if(now!=b[i])
{
for(int j=l[i];j<lst;j++) update(1,j,j,1ll*j*now-1ll*j*b[i]);
b[i]=now;
}
if(b[i]==b[find(lst)])
{
int x=find(lst),y=find(i);
f[x]=y;
l[y]=min(l[x],l[y]);
}
i=lst=l[find(i)];
}
ans++;
lst=r;
for(int i=r-1;i>0;i--)
{
i=find(i);
ans+=query(1,l[i],lst-1,1ll*b[i]*r);
i=lst=l[find(i)];
}
printf("%lld ",ans);
}
return 0;
}

浙公网安备 33010602011771号