2023.10.11~2023.10.17测试
2023.10.11
T1 染色
给定
诈骗题
观察到若
code
#pragma optimize GCC(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n;
int main()
{
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
// fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
scanf("%d",&n);
if(n==1)
printf("1\n1");
else if(n==2)
printf("1\n1 1");
else if(n==3)
printf("2\n1 1 2");
else if(n==4)
printf("2\n1 1 2 2");
else if(n==5)
printf("3\n1 1 2 2 3");
else if(n==6)
printf("3\n1 1 2 2 3 3");
else
{
puts("4");
for(int i=1; i<=n; i++)
printf("%d ",i%4+1);
}
return 0;
}
T2 序列
给定一个长度为
求
这个
从贪心来说显然是先考虑小的
所以,在钦定 打表可以发现答案是一个关于
综上,突破口在于钦定
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+10;
int T,m;
LL n,k,D,a[N],s[N];
int find2(LL mn,LL d)
{
if(m<=2)
return 0;
int lo=0,hi=m-2;
while(lo+1<hi)
{
int mid=(lo+hi)/2;
if((n-mn)*s[mid]<=d)
lo=mid;
else
hi=mid;
}
return lo;
}
LL calc(LL mn)
{
if(mn<0)
return 0;
LL res=mn*m+mn*k,d=D-mn*s[m],add=0;
if(d<0)
return 0;
LL pos=find2(mn,d);
d-=(n-mn)*s[pos];
res+=(n-mn)*pos+min(n-mn,d/a[pos+1]);
return res;
}
LL find(LL l,LL r)
{
LL lo=l,hi=r;
while(lo+2<hi)
{
LL lmid=lo+(hi-lo)/3;
LL rmid=hi-(hi-lo)/3;
if(calc(lmid)>=calc(rmid))
hi=rmid;
else
lo=lmid;
}
return (lo+hi)/2;
}
void mian()
{
scanf("%lld%d%lld%lld",&n,&m,&k,&D);
for(int i=1; i<=m; i++)
scanf("%lld",&a[i]);
sort(a+1,a+1+m);
for(int i=1; i<=m; i++)
s[i]=s[i-1]+a[i];
LL mn=find(0,min(n,D/s[m]));
LL ans=calc(mn);
for(int i=1; i<=10; i++)
ans=max(ans,max(calc(mn+i),calc(mn-i)));
printf("%lld\n",ans);
}
int main()
{
freopen("array.in","r",stdin);
freopen("array.out","w",stdout);
scanf("%d",&T);
while(T--)
mian();
return 0;
}
T3 树上询问
一棵
简单题,但是树上倍增预处理写错了
将一条
code
#pragma optimize GCC(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
bool Mbe;
const int N=3e5+10;
int n,m,a[2][N],b[2][N],rev[2][N],m0,m1;
vector <int> g[N];
namespace Seg
{
int rt[N][2];
struct SegmentTree
{
int lc[N*40],rc[N*40],sum[N*40],tot;
#define lc(x) lc[x]
#define rc(x) rc[x]
#define sum(x) sum[x]
void pushup(int p)
{
sum(p)=sum(lc(p))+sum(rc(p));
}
void build(int &p,int l,int r)
{
p=++tot;
if(l==r)
return sum(p)=0,void();
int mid=(l+r)>>1;
build(lc(p),l,mid);
build(rc(p),mid+1,r);
}
void change(int &p,int pre,int l,int r,int x,int v)
{
// cout<<l<<" "<<r<<endl;
p=++tot;
lc(p)=lc(pre); rc(p)=rc(pre); sum(p)=sum(pre);
if(l==r)
return sum(p)+=v,void();
int mid=(l+r)>>1;
if(x<=mid)
change(lc(p),lc(pre),l,mid,x,v);
else
change(rc(p),rc(pre),mid+1,r,x,v);
pushup(p);
}
int ask(int p,int pre,int l,int r,int x)
{
if(!p)
return 0;
if(l==r)
return sum(p)-sum(pre);
int mid=(l+r)>>1;
if(x<=mid)
return ask(lc(p),lc(pre),l,mid,x);
return ask(rc(p),rc(pre),mid+1,r,x);
}
}t0,t1;
}
using namespace Seg;
namespace TR
{
int dep[N],f[N][25];
void dfs1(int x,int fa)
{
dep[x]=dep[fa]+1;
for(int y:g[x])
{
if(y==fa)
continue;
f[y][0]=x;
for(int i=1; i<=20; i++)
f[y][i]=f[f[y][i-1]][i-1];
dfs1(y,x);
}
}
void dfs2(int x,int fa)
{
t0.change(rt[x][0],rt[fa][0],1,m0,a[0][x],1);
t1.change(rt[x][1],rt[fa][1],1,m1,a[1][x],1);
for(int y:g[x])
if(y!=fa)
dfs2(y,x);
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
for(int i=20; i>=0; i--)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y)
return x;
for(int i=20; i>=0; i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
}
using namespace TR;
bool Med;
int main()
{
// fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
freopen("query.in","r",stdin);
freopen("query.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1; i<n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
dfs1(1,0);
for(int i=1; i<=n; i++)
{
a[0][i]=b[0][i]=dep[i]+i;
a[1][i]=b[1][i]=dep[i]-i;
}
sort(b[0]+1,b[0]+1+n);
sort(b[1]+1,b[1]+1+n);
m0=unique(b[0]+1,b[0]+1+n)-(b[0]+1);
m1=unique(b[1]+1,b[1]+1+n)-(b[1]+1);
for(int i=1; i<=n; i++)
{
int x=lower_bound(b[0]+1,b[0]+1+m0,a[0][i])-b[0];
rev[0][x]=a[0][i]; a[0][i]=x;
int y=lower_bound(b[1]+1,b[1]+1+m1,a[1][i])-b[1];
rev[1][y]=a[1][i]; a[1][i]=y;
}
t0.build(rt[0][0],1,m0);
t1.build(rt[0][1],1,m1);
dfs2(1,0);
while(m--)
{
int l,r,lca,ans=0;
scanf("%d%d",&l,&r);
lca=LCA(l,r);
int x=lower_bound(b[0]+1,b[0]+1+m0,dep[l])-b[0];
if(b[0][x]==dep[l])
ans+=t0.ask(rt[l][0],rt[lca][0],1,m0,x);
int y=lower_bound(b[1]+1,b[1]+1+m1,2*dep[lca]-dep[l])-b[1];
if(b[1][y]==2*dep[lca]-dep[l])
ans+=t1.ask(rt[r][1],rt[lca][1],1,m1,y);
if(dep[l]-dep[lca]==lca)
ans++;
printf("%d\n",ans);
}
return 0;
}
T4 莫队
(与莫队无关)
给出一个长度为
设
一个区间
后缀
类似于 P4198 楼房重建,合并两个区间时,
具体地,设
否则,右区间贡献以
还要用个 set 维护
code
#pragma optimize GCC(3,"Ofast","inline")
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+10;
int n,m,a[N],f[N],hi;
LL res;
set <int> s[N];
#define lc(p) p<<1
#define rc(p) p<<1|1
struct SegmentTree
{
int mn; LL dat;
#define mn(x) tree[x].mn
#define dat(x) tree[x].dat
}tree[N<<2];
LL merge(int val,int p,int l,int r)
{
if(l==r)
return min(dat(p),(LL)val);
int mid=(l+r)>>1;
if(mn(rc(p))<val)
return dat(p)-dat(rc(p))+merge(val,rc(p),mid+1,r);
return merge(val,lc(p),l,mid)+1LL*val*(r-mid);
}
void pushup(int p,int l,int r)
{
mn(p)=min(mn(lc(p)),mn(rc(p)));
dat(p)=dat(rc(p))+merge(mn(rc(p)),lc(p),l,(l+r)>>1);
}
void build(int p,int l,int r)
{
if(l==r)
{
dat(p)=mn(p)=f[l];
return;
}
int mid=(l+r)>>1;
build(lc(p),l,mid);
build(rc(p),mid+1,r);
pushup(p,l,r);
}
void change(int p,int l,int r,int x,int v)
{
if(l==r)
{
dat(p)=mn(p)=v;
return;
}
int mid=(l+r)>>1;
if(x<=mid)
change(lc(p),l,mid,x,v);
else
change(rc(p),mid+1,r,x,v);
pushup(p,l,r);
}
void ask(int p,int l,int r,int ql,int qr)
{
if(ql<=l && qr>=r)
{
res+=merge(hi,p,l,r);
hi=min(hi,mn(p));
return;
}
int mid=(l+r)>>1;
if(qr>mid)
ask(rc(p),mid+1,r,ql,qr);
if(ql<=mid)
ask(lc(p),l,mid,ql,qr);
}
int main()
{
freopen("team.in","r",stdin);
freopen("team.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]),s[i].insert(n+1);
for(int i=n; i>=1; i--)
f[i]=*(s[a[i]].begin()),s[a[i]].insert(i);
build(1,1,n);
while(m--)
{
int op,l,r,x,y;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d",&x,&y);
auto it=s[a[x]].find(x);
int nxt=*(++it); it--;
if(it!=s[a[x]].begin())
it--,change(1,1,n,*it,nxt);
s[a[x]].erase(x); a[x]=y;
it=s[y].lower_bound(x);
change(1,1,n,x,*it);
if(it!=s[y].begin())
it--,change(1,1,n,*it,x);
s[y].insert(x);
}
else
{
scanf("%d%d",&l,&r);
res=0; hi=r+1;
ask(1,1,n,l,r);
res-=1LL*(r-l+1)*(l+r)/2;
printf("%lld\n",res);
}
}
return 0;
}
2023.10.12
联考?666
T1 异或
有一个
执行
就是给一个等腰直角三角形全部加上
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e3+10;
int n,q;
LL b[N][N],d[N][N],ans;
int main()
{
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
scanf("%d%d",&n,&q);
while(q--)
{
int r,c,l,s,tx=0,ty=0;
scanf("%d%d%d%d",&r,&c,&l,&s);
tx=r+l-1; ty=tx-r+c;
d[r][c]+=(LL)s; d[tx+1][c]-=(LL)s;
b[tx+1][c+1]-=(LL)s; b[tx+1][ty+2]+=(LL)s;
}
for(int i=0; i<=n+2; i++)
{
for(int j=1; j<=n+2; j++)
d[j][i]+=d[j-1][i];
}
for(int i=0; i<=n+2; i++)
{
for(int j=1; j<=n+2; j++)
b[i][j]+=b[i][j-1];
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
d[i][j]+=d[i-1][j-1]+b[i][j];
ans^=d[i][j];
}
}
printf("%lld",ans);
return 0;
}
T2 游戏
Alice 和 Bob 在博弈,共
一开始有集合
Alice 想要让最后
博弈一点不会好吧。写个部分分还因为判断条件写错挂成
最后我们需要一点观察,我们发现每次操作中如果先手选择大的那一侧,那么至多
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e4+10,M=2e5+10;
int n,m,tot,rt;
LL a[N],b[M];
struct Tree
{
int lc,rc; LL sum;
#define lc(x) tree[x].lc
#define rc(x) tree[x].rc
#define sum(x) tree[x].sum
}tree[N<<2];
void insert(int &p,int dep,LL x)
{
if(!p)
p=++tot;
if(dep==m+1)
return sum(p)+=x,void();
if(x%b[dep]==0)
insert(lc(p),dep+1,x);
else
insert(rc(p),dep+1,x);
}
LL query(int p,int dep)
{
if(!p)
return 0;
if(dep==m+1)
return sum(p);
LL lval=query(lc(p),dep+1);
LL rval=query(rc(p),dep+1);
if(dep&1)
return min(lval,rval);
return max(lval,rval);
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%lld",&a[i]);
for(int i=1; i<=m; i++)
scanf("%lld",&b[i]);
if(m>2*(log2(n)+1))
{
printf("0");
return 0;
}
for(int i=1; i<=n; i++)
insert(rt,1,a[i]);
printf("%lld",query(rt,1));
return 0;
}
T3 联通块
一个
考场上有差不多的想法,但是不完善,暴搜数组开小只拿了
暴力来说建边和计算都是
考虑快速建边,定义单位合数为可以表示成两个质数乘积的合数,对每个单位合数建一个虚点,令每个点与自己的单位合数因数连边,可以快速建边。
对于计算,首先观察可发现肯定是割掉最大联通块里的一个割点,最后再计算最大联通块。割点用 Tarjan 求即可
但是 Tarjan 求割点到底写不写栈?我也不清楚,不写栈是错的,那就写栈吧
code
#pragma optimize GCC(3,"Ofast","inline")
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=4e6+10,M=1e7+10,R=1e7+10;
int T,n,a[N],mxa,ans;
int prime[R],v[R],tp,id[R],tt;
int dfn[N],low[N],cut[N],siz[N],cnt,sta[N],top;
int head[N],ver[M],nxt[M],tot=1,node;
int fa[N],wei[N];
void prework()
{
for(int i=2; i<=1e7; i++)
{
if(!v[i])
{
prime[++tp]=i;
v[i]=i;
}
for(int j=1; j<=tp; j++)
{
if(prime[j]>v[i] || prime[j]>1e7/i)
break;
v[i*prime[j]]=prime[j];
}
}
for(int i=2; i<=1e7; i++)
if(v[i]!=i && v[i/v[i]]==i/v[i])
id[i]=++tt;
}
void init()
{
tot=1; mxa=cnt=top=0;
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(cut,0,sizeof(cut));
memset(siz,0,sizeof(siz));
}
int get(int x)
{
if(x==fa[x])
return x;
return fa[x]=get(fa[x]);
}
void merge(int x,int y)
{
int fx=get(x),fy=get(y);
if(fx==fy)
return;
fa[fx]=fy; wei[fy]+=wei[fx];
}
void add(int x,int y)
{
ver[++tot]=y; nxt[tot]=head[x]; head[x]=tot;
ver[++tot]=x; nxt[tot]=head[y]; head[y]=tot;
}
void add_G()
{
node=tt+n;
for(int i=1; i<=n; i++)
{
int p[15]={0},c[15]={0};
int x=a[i];
while(x>1)
{
int w=v[x];
p[++p[0]]=w;
while(x%w==0)
c[p[0]]++,x/=w;
}
for(int j=1; j<=p[0]; j++)
{
for(int k=j+(c[j]<=1); k<=p[0]; k++)
{
if(1LL*p[j]*p[k]<1e7)
{
int v=id[p[j]*p[k]]+n;
add(i,v);
merge(i,v);
}
}
}
}
}
void tarjan(int x,int root,int large)
{
dfn[x]=low[x]=++cnt;
sta[++top]=x;
if(x<=n)
siz[x]=1;
int flag=0,sum=0,res=1;
for(int i=head[x]; i; i=nxt[i])
{
int y=ver[i];
if(!dfn[y])
{
tarjan(y,root,large);
low[x]=min(low[x],low[y]);
if(dfn[x]<=low[y])
{
flag++;
if(flag>1 || x!=root)
cut[x]=1;
}
if(dfn[x]==low[y])
{
int z,s=0;
do
{
z=sta[top--];
siz[x]+=siz[z];
s+=siz[z];
}while(z!=y);
res=max(res,s);
}
}
else
low[x]=min(low[x],dfn[y]);
}
if(cut[x])
res=max(res,large-siz[x]);
else
res=large-1;
if(x<=n)
ans=min(ans,res);
}
void mian()
{
init();
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=1; i<=4e6; i++)
fa[i]=i,wei[i]=(i<=n);
add_G();
int mx=0,rt=0,se=0;
for(int i=1; i<=node; i++)
{
if(fa[i]!=i || !wei[i])
continue;
if(wei[i]>mx)
se=mx,mx=wei[i],rt=i;
else if(wei[i]>se)
se=wei[i];
}
ans=mx;
tarjan(rt,rt,mx);
printf("%d\n",max(ans,se));
}
int main()
{
freopen("connect.in","r",stdin);
freopen("connect.out","w",stdout);
prework();
scanf("%d",&T);
while(T--)
mian();
return 0;
}
2023.10.13
(牛客场)
T1 矩阵交换
一个
签到题,写了
code
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N=110;
int T,n,m,a[N][N],cnt;
struct node{int id,val;}ans[N];
vector <pii> pos;
pii tmp[N];
void init()
{
memset(a,0,sizeof(a));
int len=pos.size();
for(int i=0; i<len; i++)
pos.pop_back();
}
void mian()
{
init();
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
ans[i].id=i;
for(int j=1; j<=m; j++)
scanf("%d",&a[i][j]);
}
pos.push_back({1,n});
for(int i=1; i<=m; i++)
{
for(int j=1; j<=n; j++)
ans[j].val=a[ans[j].id][i];
cnt=0;
for(auto v:pos)
{
sort(ans+v.first,ans+v.second+1,[](node x,node y){return x.val<y.val;});
int l=v.first,r=v.first;
for(int j=v.first+1; j<=v.second; j++)
{
r++;
if(a[ans[j].id][i]!=a[ans[j-1].id][i])
tmp[++cnt]={l,r-1},l=r;
}
tmp[++cnt]={l,r};
}
int len=pos.size();
for(int j=0; j<len; j++)
pos.pop_back();
for(int j=1; j<=cnt; j++)
pos.push_back(tmp[j]);
}
for(int i=1; i<=m; i++)
{
for(int j=2; j<=n; j++)
{
if(a[ans[j].id][i]<a[ans[j-1].id][i])
{
printf("NO\n");
return;
}
}
}
printf("YES\n");
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d",&T);
while(T--)
mian();
return 0;
}
T2 砖块摆放
原题??[ARC117C] Tricolor Pyramid
好题
这个操作很难直接做,所以考虑将操作转化成一些数字上的操作
一个牛逼的转化是令
而我在考场上贺了 SError 的做法,令
然后通过枚举与打表可以发现,对于第
记
代码不难写
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+10,MOD=7;
int T,n,a[N];
char s[N];
int c[5][5];
int ksm(int x,int y)
{
int res=1;
while(y)
{
if(y&1)
res=1LL*res*x%MOD;
x=1LL*x*x%MOD;
y>>=1;
}
return res;
}
void prework()
{
c[0][0]=1;
c[1][0]=c[1][1]=1;
c[2][0]=1; c[2][1]=2; c[2][2]=1;
c[3][0]=1; c[3][1]=c[3][2]=3; c[3][3]=1;
}
int C(int x,int y,int p)
{
return c[x][y];
}
int lucas(int x,int y,int p)
{
if(y==0)
return 1;
if(x<p && y<p)
return C(x%p,y%p,p);
return 1LL*C(x%p,y%p,p)*lucas(x/p,y/p,p)%p;
}
int crt(int a1,int a2)
{
int m=6,m1=2,m2=3;
int M1=3,M2=2;
int t1=ksm(M1,m1-2),t2=ksm(M2,m2-2);
int ans=1LL*(1LL*a1*M1%m*t1%m+1LL*a2*M2%m*t2%m)%m;
return ans;
}
void mian()
{
scanf("%d%s",&n,s+1);
for(int i=1; i<=n; i++)
{
if(s[i]=='A')
a[i]=1;
else if(s[i]=='B')
a[i]=2;
else
a[i]=4;
}
int ans=1;
for(int i=1; i<=n; i++)
{
int ind1=lucas(n-1,i-1,2),ind2=lucas(n-1,i-1,3),res=1;
int ind=crt(ind1,ind2);
res=ksm(a[i],ind);
if(n%2==0)
res=ksm(res,MOD-2);
ans=1LL*ans*res%MOD;
}
if(ans==1)
puts("A");
else if(ans==2)
puts("B");
else
puts("C");
}
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
prework();
scanf("%d",&T);
while(T--)
mian();
return 0;
}
T3 学习 LIS
有一个长度为
现在给定
很难
首先
考虑计算方案,设
再把枚举子集的过程拉出来,设
考虑容斥,则有
预处理一些东西可以做到
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=25,M=3010,SS=(1<<20)+10,MOD=998244353;
int n,m,a[N],pre[SS];
int C[M][M],f[2][N][SS],ans[N];
void prework()
{
for(int i=0; i<=m; i++)
C[i][0]=1;
for(int i=1; i<=m; i++)
for(int j=1; j<=i; j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
int S=(1<<n)-1;
for(int s=0; s<=S; s++)
{
for(int i=1; i<=n; i++)
if(s&(1<<i-1))
pre[s]=max(pre[s],a[i]);
}
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
prework();
int S=(1<<n)-1,cur=1,tur=0;
f[cur][n][0]=1;
for(int t=1; t<=n; t++)
{
for(int i=n; i>=1; i--)
{
for(int s=0; s<=S; s++)
{
if(f[cur][i][s])
{
int tmp=f[cur][i][s];
if(i>1)
(f[cur][i-1][s]+=tmp)%=MOD;
else
(f[tur][n][s]+=tmp)%=MOD;
if(!(s&(1<<i-1)) && pre[s&((1<<i-1)-1)]+1==a[i])
{
if(i>1)
(f[cur][i-1][s|(1<<i-1)]+=tmp)%=MOD;
else
(f[tur][n][s|(1<<i-1)]+=tmp)%=MOD;
}
f[cur][i][s]=0;
}
}
}
ans[t]=f[tur][n][S];
swap(cur,tur);
}
int sum=0;
for(int i=1; i<=n; i++)
{
for(int j=i-1; j>=1; j--)
(ans[i]+=(1LL*(-1)*C[i][j]*ans[j]%MOD+MOD))%=MOD;
(sum+=1LL*C[m][i]*ans[i]%MOD)%=MOD;
}
printf("%d",sum);
return 0;
}
2023.10.16
T1 智乃的差分
2023.10.17
计数场
T1 序列计数
有
定义一种照片的混乱度为照片中相邻但种类不同的奶牛的对数。若将一张照片中的所有奶牛任意改变顺序,而原图的混乱度仍是最小(或之一)的话,则称这张照片是“好的”,求共能拍出多少张“好的照片
签到题,只拿了 60 分……
一开始设
果然还是状态设的不够好,设
则有
滚个前缀和优化即可,空间很小要用滚动数组
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1010,M=(1<<10)+10,MOD=998244353;
bool Mbe;
int T,n,a[N],tot,t[N][15],f[2][M][15],g[2][M];
char s[N];
map <char,int> id;
void init()
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
tot=0;
id.clear();
}
void mian()
{
init();
scanf("%d%s",&n,s+1);
for(int i=1; i<=n; i++)
{
if(id.find(s[i])==id.end())
{
id[s[i]]=++tot;
a[i]=tot;
}
else
a[i]=id[s[i]];
}
f[0][0][0]=g[0][0]=1; int S=(1<<tot)-1;
for(int i=1; i<=n; i++)
{
for(int s=0; s<=S; s++)
{
g[i&1][s]=0;
for(int k=1; k<=tot; k++)
{
f[i&1][s][k]=0;
if(!(s&(1<<k-1)))
continue;
if(a[i]==k)
(f[i&1][s][k]+=g[(i&1)^1][s^(1<<k-1)]+f[(i&1)^1][s][k])%=MOD;
(f[i&1][s][k]+=f[(i&1)^1][s][k])%=MOD;
(g[i&1][s]+=f[i&1][s][k])%=MOD;
}
// cout<<i<<" "<<s<<" "<<g[i][s]<<endl;
}
f[i&1][0][0]=g[i&1][0]=1;
}
int ans=0;
for(int s=1; s<=S; s++)
(ans+=g[n&1][s])%=MOD;
// int ans=0;
// for(int s=1; s<=S; s++)
// (ans+=f[s][n])%=MOD;//,cout<<s<<" "<<f[s][n]<<endl;
printf("%d\n",ans);
}
bool Med;
int main()
{
freopen("counta.in","r",stdin);
freopen("counta.out","w",stdout);
// fprintf(stderr,"%.3lfMB\n",(&Mbe-&Med)/1048576.0);
scanf("%d",&T);
while(T--)
mian();
return 0;
}
T2 子段计数
CF1736C2 Good Subarrays (Hard Version)
再也不写线段树二分了!!!!!!
设
对于操作
-
:此时 -
:这时会有一段区间越不过 了,将对应贡献减去即可,是简单的 -
:此时会有原先一段越不过 的区间越过了 。考虑预处理一个数组 表示在忽略 的情况下以 为左端点最远能到达哪里,这也是好预处理的。 即
显然你用兔队线段树也可以
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+10,INF=1e18;
int n,t[N],a[N],b[N],m;
LL ans,sum[N],sum2[N];
#define lc(p) p<<1
#define rc(p) p<<1|1
struct SegmentTree
{
LL mx;
#define mx(x) tree[x].mx
}tree[N<<2];
void pushup(int p)
{
mx(p)=max(mx(lc(p)),mx(rc(p)));
}
void build(int p,int l,int r)
{
mx(p)=-INF;
if(l==r)
{
mx(p)=(LL)a[l];
return;
}
int mid=(l+r)>>1;
build(lc(p),l,mid);
build(rc(p),mid+1,r);
pushup(p);
}
LL ask(int p,int l,int r,int ql,int qr)
{
if(ql<=l && qr>=r)
return mx(p);
int mid=(l+r)>>1;
LL res=-INF;
if(ql<=mid)
res=max(res,ask(lc(p),l,mid,ql,qr));
if(qr>mid)
res=max(res,ask(rc(p),mid+1,r,ql,qr));
return res;
}
int find(int l,int r,int v)
{
int lo=l,hi=r,res=lo-1;
while(lo<=hi)
{
int mid=(lo+hi)>>1;
if(ask(1,1,n,lo,mid)<v)
lo=mid+1,res=mid;
else
hi=mid-1;
}
return res;
}
int find2(int l,int r,int v)
{
int lo=l-1,hi=r+1;
while(lo+1<hi)
{
int mid=(lo+hi)>>1;
if(a[mid]>=v)
hi=mid;
else
lo=mid;
}
if(a[lo]>=v && lo!=0)
return lo;
return hi;
}
LL get(int l,int r)
{
LL res1=1LL*l*(l-1)/2;
LL res2=1LL*(1+r)*r/2;
return res2-res1;
}
int main()
{
// freopen("countb4.in","r",stdin);
// freopen("countb.out","w",stdout);
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&t[i]),a[i]=i-t[i];
build(1,1,n);
for(int i=1; i<=n; i++)
{
a[i]=find(i,n,i);
ans+=1LL*(a[i]-i+1);
sum[i]=sum[i-1]+1LL*(a[i]-i+1);
if(a[i]==n)
b[i]=n;
else if(a[i]==n-1)
b[i]=n;
else
{
b[i]=find(a[i]+2,n,i);
if(b[i]>n)
b[i]=n;
}
sum2[i]=sum2[i-1]+1LL*(b[i]-i+1);
}
scanf("%d",&m);
while(m--)
{
int x,v;
scanf("%d%d",&x,&v);
if(t[x]==v)
{
printf("%lld\n",ans);
continue;
}
int pos=find2(1,x,x),l=max(x-v,0);
if(t[x]>v)
{
if(l<pos)
{
printf("%lld\n",ans);
continue;
}
LL pre=sum[l]-sum[pos-1];
LL cur=1LL*(l-pos+1)*x-get(pos,l);
LL res=ans-pre+cur;
printf("%lld\n",res);
}
else
{
int pos2=find2(1,x-1,x-1);
if(pos2<=l)
pos2=l+1;
LL res=ans-sum[pos-1]+sum[pos2-1];
res=res+sum2[pos-1]-sum2[pos2-1];
printf("%lld\n",res);
}
}
return 0;
}
T3 组合计数
CF1307E Cow and Treats 的加强版,
先讨论弱化版
预处理
考虑枚举第一头从左往右走的奶牛
显然一种颜色的奶牛最多只能有
-
: 的个数 -
: 的个数 -
: 的个数
容易发现
对于
-
若
或者 且 不都为 ,则可以有两头奶牛,方案数为 -
否则,若
不都为 ,则可以有一头奶牛,方案数为
对于
显然
时间复杂度
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=5010,MOD=1e9+7;
int n,m,c[N],f[N],h[N],L[N],R[N],sum[N];
vector <int> col[N],cow[N];
int calc(int x,int p)
{
int cnt=(x>0),res=1;
for(int i=1; i<=n; i++)
{
int s=0,sl=0,sr=0;
for(int j:cow[i])
{
if(j==x)
continue;
if(L[j]<p && R[j]>p)
s++;
else if(L[j]<p)
sl++;
else if(R[j]>p)
sr++;
}
if(f[x]==i)
{
if(s+sr>=1)
cnt++,res=1LL*res*(s+sr)%MOD;
}
else if((s>=2) || (s==1 && sl+sr>=1))
cnt+=2,res=1LL*res*s%MOD*(s-1+sl+sr)%MOD;
else if(s+sl+sr>=1)
cnt++,res=1LL*res*(2*s+sl+sr)%MOD;
}
(sum[cnt]+=res)%=MOD;
return cnt;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%d",&c[i]),col[c[i]].push_back(i);
for(int i=1; i<=m; i++)
{
scanf("%d%d",&f[i],&h[i]);
if(h[i]>col[f[i]].size())
continue;
L[i]=col[f[i]][h[i]-1];
R[i]=col[f[i]][col[f[i]].size()-h[i]];
cow[f[i]].push_back(i);
}
int ans=calc(0,0);
for(int i=1; i<=n; i++)
for(int j:cow[i])
ans=max(ans,calc(j,L[j]));
printf("%d %d",ans,sum[ans]);
return 0;
}
再来考虑强化版
我们发现每次枚举
我们可以先让所有奶牛从右边走,可以计算出此时的奶牛数和方案数。之后,将断点右移,对于
具体来说,设
之后,从左到右枚举断点,先将当前颜色的贡献减去,再重新计算
最后,将
时间复杂度
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+10,MOD=1e9+7;
int T,n,m,c[N],f[N],h[N],L[N],R[N];
int cnt,cur,s[N][5],dir[N],val[N],sum[N],ans;
vector <int> col[N],posl[N],posr[N];
bool vis[N];
void init()
{
for(int i=1; i<=n; i++)
posl[i].clear(),posr[i].clear(),col[i].clear();
memset(vis,0,sizeof(vis));
memset(s,0,sizeof(s));
memset(sum,0,sizeof(sum));
}
int ksm(int x,int y)
{
int res=1;
while(y)
{
if(y&1)
res=1LL*res*x%MOD;
x=1LL*x*x%MOD;
y>>=1;
}
return res;
}
int get(int x)
{
if(s[x][3]>=2 || (s[x][3]==1 && s[x][1]+s[x][2]>=1))
return 2;
if(s[x][3]+s[x][2]+s[x][1]>=1)
return 1;
return 0;
}
void add(int x,int y)
{
int cc=f[x],tmp=get(cc),res=1;
cnt+=y*tmp;
if(tmp==2)
res=1LL*s[cc][3]*(s[cc][3]-1+s[cc][2]+s[cc][1])%MOD;
else if(tmp==1)
res=1LL*(2*s[cc][3]+s[cc][2]+s[cc][1])%MOD;
if(y==-1)
res=ksm(res,MOD-2);
val[cc]=1LL*val[cc]*res%MOD;
cur=1LL*cur*res%MOD;
}
void update(int x,int y)
{
add(x,-1); s[f[x]][dir[x]]--;
dir[x]=y;
s[f[x]][dir[x]]++; add(x,1);
}
void mian()
{
init();
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%d",&c[i]),col[c[i]].push_back(i),val[i]=1;
for(int i=1; i<=m; i++)
{
scanf("%d%d",&f[i],&h[i]);
if(h[i]>col[f[i]].size())
continue;
L[i]=col[f[i]][h[i]-1];
R[i]=col[f[i]][col[f[i]].size()-h[i]];
posl[L[i]].push_back(i);
posr[R[i]].push_back(i);
s[f[i]][dir[i]=2]++;
}
cnt=0; cur=1;
for(int i=1; i<=m; i++)
if(!vis[f[i]])
vis[f[i]]=1,add(i,1);
sum[ans=cnt]=cur;
for(int i=0; i<=n; i++)
{
int c=::c[i];
if(posl[i].size())
{
int x=posl[i][0],delta=s[c][3]+s[c][2]-(R[x]>i);
cnt-=get(c);
cur=1LL*cur*ksm(val[c],MOD-2)%MOD;
int nowcnt=cnt+1+(delta>0),deltacur=max(1,delta);
ans=max(ans,nowcnt);
(sum[nowcnt]+=1LL*cur*deltacur%MOD)%=MOD;
cnt+=get(c);
cur=1LL*cur*val[c]%MOD;
}
for(int x:posl[i])
update(x,dir[x]^1);
for(int x:posr[i+1])
update(x,dir[x]^2);
}
printf("%d %d\n",ans,sum[ans]);
}
int main()
{
freopen("countc.in","r",stdin);
freopen("countc.out","w",stdout);
scanf("%d",&T);
while(T--)
mian();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具