以前做过:这里。
直接单调队列做即可。
#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=2000010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,d,a[Maxn],Log[Maxn];
LL p,sum[Maxn],b[Maxn];
int q[Maxn];
int main()
{
n=read();scanf("%lld",&p);d=read();
sum[0]=0;
for(int i=1;i<=n;i++)
{
a[i]=read();
sum[i]=sum[i-1]+(LL)(a[i]);
}
LL t=0;int len=1;
for(int i=1;i<=d;i++)t+=(LL)(a[i]);
b[1]=t;
for(int i=d+1;i<=n;i++)
{
t=t-(LL)(a[i-d])+(LL)(a[i]);
b[++len]=t;
}
int ans=d,head=1,tail=1,l=1;q[1]=1;
for(int i=d+1;i<=n;i++)
{
int t=i-d+1;
while(head<=tail&&b[t]>b[q[tail]])tail--;
q[++tail]=t;
while(l<i&&sum[i]-sum[l-1]-b[q[head]]>p)
{
if(q[head]==l)head++;
l++;
}
ans=max(ans,i-l+1);
}
printf("%d",ans);
}
显然对于询问,每个数 x x x的贡献为 min ( x , S ) \min(x,S) min(x,S),所以只要用树状数组维护一下区间和就可以了。
#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=1000010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,m,to[Maxn],a[Maxn];
struct Opt{int op;LL x,y;}q[Maxn];
struct Node{int x,id;}A[Maxn];int la=0,cnt=0;
bool cmp(Node a,Node b){return a.x<b.x;}
struct BIT
{
LL s[Maxn];
void add(int x,int y){for(;x<=cnt;x+=(x&-x))s[x]+=y;}
LL query(int x){LL re=0;for(;x;x-=(x&-x))re+=s[x];return re;}
}t1,t2;
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
char ss[2];scanf("%s",ss);
if(ss[0]=='U')q[i].op=1;
else q[i].op=2;
q[i].x=read(),q[i].y=read();
if(q[i].op==1)A[++la].x=q[i].y,A[la].id=i;
}
A[++la].x=0,A[la].id=-1;
sort(A+1,A+1+la,cmp);
for(int i=1;i<=la;i++)
{
if(i==1||A[i].x!=A[i-1].x)cnt++;
to[cnt]=A[i].x;
if(A[i].id!=-1)q[A[i].id].y=cnt;
}
for(int i=1;i<=n;i++)a[i]=1;
t1.add(1,n);
for(int i=1;i<=m;i++)
{
if(q[i].op==2)
{
int l=1,r=cnt;
while(l<=r)
{
int mid=l+r>>1;
if(to[mid]<=q[i].y)l=mid+1;
else r=mid-1;
}
l--;
LL t=q[i].y*(t1.query(cnt)-t1.query(l))+t2.query(l);
if(t>=q[i].x*q[i].y)puts("TAK");
else puts("NIE");
}
else
{
t1.add(a[q[i].x],-1);
t2.add(a[q[i].x],-to[a[q[i].x]]);
a[q[i].x]=q[i].y;
t1.add(a[q[i].x],1);
t2.add(a[q[i].x],to[a[q[i].x]]);
}
}
}
看到这个数据范围肯定想根号分治。
对于步长 > n >\sqrt n >n,跳的次数不会超过 n \sqrt n n,利用长链剖分 O ( 1 ) O(1) O(1)找 k k k级祖先即可。
对于步长 ≤ n \le\sqrt n ≤n,直接预处理 s [ i ] [ j ] s[i][j] s[i][j]表示从 i i i开始往上所有与 i i i深度差为 j j j的倍数的点权和,即可快速求答案。
#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=50010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,m,a[Maxn],dep[Maxn],fa[Maxn][16],son[Maxn],mx[Maxn],top[Maxn],ll[Maxn],pp[Maxn],s[Maxn][230];
struct Edge{int y,next;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y)
{
int t=++len;
e[t].y=y;e[t].next=last[x];last[x]=t;
}
vector<int>up[Maxn],down[Maxn];
void dfs1(int x,int f)
{
mx[x]=dep[x]=dep[f]+1;fa[x][0]=f;son[x]=0;
for(int i=1;(1<<i)<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==f)continue;
dfs1(y,x);
if(mx[y]>mx[son[x]])son[x]=y;
mx[x]=max(mx[x],mx[y]);
}
}
void dfs2(int x,int tp)
{
top[x]=tp;
if(son[x])dfs2(son[x],tp);
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==fa[x][0]||y==son[x])continue;
dfs2(y,y);
}
if(!son[x])ll[x]=1;
else ll[x]=ll[son[x]]+1;
if(top[x]==x)
{
int c,t;
c=ll[x],t=x;while(c--)up[x].push_back(t),t=fa[t][0];
c=ll[x],t=x;while(c--)down[x].push_back(t),t=son[t];
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=15;i>=0;i--)
if((1<<i)<=dep[x]-dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=15;i>=0;i--)
if((1<<i)<=dep[x]&&fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int query(int x,int k)
{
if(!k)return x;
x=fa[x][pp[k]],k-=(1<<pp[k]);
if(dep[top[x]]>=dep[x]-k)return up[top[x]][dep[top[x]]-dep[x]+k];
return down[top[x]][dep[x]-k-dep[top[x]]];
}
void dfs3(int x)
{
for(int i=1;i<=m;i++)
{
s[x][i]=a[x];
if(i<=dep[x])s[x][i]+=s[query(x,i)][i];
}
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==fa[x][0])continue;
dfs3(y);
}
}
int b[Maxn];
int main()
{
n=read();m=(int)sqrt(n);
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
ins(x,y),ins(y,x);
}
int tmp=0;
for(int i=1;i<=n;i++)
{
if(i>=(1<<(tmp+1)))tmp++;
pp[i]=tmp;
}
dep[0]=-1;dfs1(1,0);
dfs2(1,1);
dfs3(1);
for(int i=1;i<=n;i++)b[i]=read();
for(int i=1;i<n;i++)
{
int c=read(),x=b[i],y=b[i+1],z=LCA(x,y),ans=0;
if(c<=m)
{
int t=(dep[x]-dep[z])/c,p=query(x,t*c);
ans+=s[x][c]-s[p][c]+a[p];
if(z==y)
{
if(p!=y)printf("%d\n",ans+a[y]);
else printf("%d\n",ans);
continue;
}
ans+=a[y];
if(dep[p]+dep[y]-(dep[z]<<1)>c)
{
int st=query(y,dep[y]-dep[z]-(c-(dep[p]-dep[z]))),ed=query(y,dep[y]-dep[st]-(dep[y]-dep[st]-1)/c*c);
ans+=s[ed][c]-s[st][c]+a[st];
}
printf("%d\n",ans);
}
else
{
while(dep[x]-dep[z]>=c)ans+=a[x],x=query(x,c);
ans+=a[x];
if(z==y)
{
if(x!=y)ans+=a[y];
printf("%d\n",ans);
continue;
}
ans+=a[y];
if(dep[x]+dep[y]-(dep[z]<<1)>c)
{
int tmp=dep[y]-dep[z]-(c-(dep[x]-dep[z]));
x=query(y,tmp);
int cc=0;
while(1)
{
ans+=a[x];
if(dep[y]-dep[x]<=c)break;
cc++;x=query(y,tmp-cc*c);
}
}
printf("%d\n",ans);
}
}
}
栋老师题解。
#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=55;
const int Maxm=4010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,m,cnt=0,to[Maxm],d[Maxn][Maxn][Maxm],ans[Maxn];
struct Node{int a,b,c;}A[Maxm];
bool cmp(Node x,Node y){return x.c<y.c;}
int f[Maxn][Maxn][Maxm],ff[Maxn][Maxn][Maxm],g[Maxn][Maxn][Maxm],gg[Maxn][Maxn][Maxm];
int h(int l,int r,int p,int k)
{return d[p][r][k]-d[p][p-1][k]-d[l-1][r][k]+d[l-1][p-1][k];}
void dfs(int l,int r,int p)
{
if(l>r)return;
if(l==r){ans[l]=to[p];return;}
int t=ff[l][r][p];
ans[t]=to[p];
dfs(l,t-1,gg[l][t-1][p]),dfs(t+1,r,gg[t+1][r][p]);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)A[i].a=read(),A[i].b=read(),A[i].c=read();
sort(A+1,A+1+m,cmp);
for(int i=1;i<=m;i++)
{
if(i==1||A[i].c!=A[i-1].c)cnt++;
to[cnt]=A[i].c;A[i].c=cnt;
}
for(int i=1;i<=m;i++)d[A[i].a][A[i].b][A[i].c]++;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=cnt-1;k;k--)
d[i][j][k]+=d[i][j][k+1];
for(int i=1;i<=n;i++)
for(int j=2;j<=n;j++)
for(int k=1;k<=cnt;k++)
d[i][j][k]+=d[i][j-1][k];
for(int i=2;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=cnt;k++)
d[i][j][k]+=d[i-1][j][k];
for(int l=1;l<=n;l++)
for(int i=1;i+l-1<=n;i++)
{
int j=i+l-1;
for(int k=cnt;k;k--)
{
if(l==1)
{
f[i][j][k]=h(i,j,i,k)*to[k];
if(f[i][j][k]>=g[i][j][k+1])gg[i][j][k]=k;
else gg[i][j][k]=gg[i][j][k+1];
g[i][j][k]=max(f[i][j][k],g[i][j][k+1]);
continue;
}
int t1=g[i+1][j][k]+h(i,j,i,k)*to[k],t2=g[i][j-1][k]+h(i,j,j,k)*to[k];
if(t1>t2)ff[i][j][k]=i;
else ff[i][j][k]=j;
f[i][j][k]=max(t1,t2);
for(int p=i+1;p<j;p++)
{
int t=g[i][p-1][k]+g[p+1][j][k]+h(i,j,p,k)*to[k];
if(t>f[i][j][k])ff[i][j][k]=p,f[i][j][k]=t;
}
if(f[i][j][k]>=g[i][j][k+1])gg[i][j][k]=k;
else gg[i][j][k]=gg[i][j][k+1];
g[i][j][k]=max(f[i][j][k],g[i][j][k+1]);
}
}
printf("%d\n",g[1][n][1]);
dfs(1,n,gg[1][n][1]);
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
}
这题的做法有很多。
1、利用两个结论,最好写,但是最难想。
2、把三种字母出现次数前缀和差分后,每个位置都是个三元组,没有任意两种颜色出现次数相同等价于两个三元组三个对应元素都不相同,看上去是个三维偏序,但是可以第一维排序,树状数组以第二维为下标,维护位置取最小值、次小值时的第三维的值,大概就是这样做,复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。
3、线性做法,考虑如果一个位置的三元组三个值都不为 0 0 0,那么显然取到开头都是合法的,否则根据三元组有多少个值为 0 0 0,可以分类讨论,对于每种情况,最多只有 4 4 4个位置是有用的,记下这些位置,直接与这些位置比较即可。
我写了做法三。
#include
using namespace std;
#define LL long long
const int Maxn=1000010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,ans=1;char s[Maxn];
struct Node
{
int a,b,c;
}A[Maxn];
int p,pab[2],pac[2],pbc[2];
int pa[4],la,pb[4],lb,pc[4],lc;
void solve()
{
pab[0]=pab[1]=pac[0]=pac[1]=pbc[0]=pbc[1]=0;la=lb=lc=-1;
int s1=0,s2=0,s3=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='C')s1++;
else if(s[i]=='S')s2++;
else s3++;
A[i].a=s1-s2,A[i].b=s2-s3,A[i].c=s1-s3;
if(A[i].a&&A[i].b&&A[i].c)ans=max(ans,i);
if(!A[i].a&&!A[i].b)
{
if(pab[0]&&A[pab[0]].c!=A[i].c)ans=max(ans,i-pab[0]);
else if(pab[1]&&A[pab[1]].c!=A[i].c)ans=max(ans,i-pab[1]);
}
if(!A[i].a&&!A[i].c)
{
if(pac[0]&&A[pac[0]].b!=A[i].b)ans=max(ans,i-pac[0]);
else if(pac[1]&&A[pac[1]].b!=A[i].b)ans=max(ans,i-pac[1]);
}
if(!A[i].c&&!A[i].b)
{
if(pbc[0]&&A[pbc[0]].a!=A[i].a)ans=max(ans,i-pbc[0]);
else if(pbc[1]&&A[pbc[1]].a!=A[i].a)ans=max(ans,i-pbc[1]);
}
if(!A[i].a)
{
for(int j=0;j<=la;j++)
if(A[pa[j]].b!=A[i].b&&A[pa[j]].c!=A[i].c)ans=max(ans,i-pa[j]);
}
if(!A[i].b)
{
for(int j=0;j<=lb;j++)
if(A[pb[j]].a!=A[i].a&&A[pb[j]].c!=A[i].c)ans=max(ans,i-pb[j]);
}
if(!A[i].c)
{
for(int j=0;j<=lc;j++)
if(A[pc[j]].a!=A[i].a&&A[pc[j]].b!=A[i].b)ans=max(ans,i-pc[j]);
}
if(A[i].a&&A[i].b&&A[i].c&&!p)p=i;
if(A[i].a&&A[i].b)
{
if(!pab[0])pab[0]=i;
else if(!pab[1])pab[1]=i;
}
if(A[i].a&&A[i].c)
{
if(!pac[0])pac[0]=i;
else if(!pac[1])pac[1]=i;
}
if(A[i].c&&A[i].b)
{
if(!pbc[0])pbc[0]=i;
else if(!pbc[1])pbc[1]=i;
}
if(A[i].a&&la<3)
{
int cnt=0;
for(int j=0;j<=la;j++)
cnt+=(A[i].b==A[pa[j]].b)+(A[i].c==A[pa[j]].c);
if(cnt<2)pa[++la]=i;
}
if(A[i].b&&lb<3)
{
int cnt=0;
for(int j=0;j<=lb;j++)
cnt+=(A[i].a==A[pb[j]].a)+(A[i].c==A[pb[j]].c);
if(cnt<2)pb[++lb]=i;
}
if(A[i].c&&lc<3)
{
int cnt=0;
for(int j=0;j<=lc;j++)
cnt+=(A[i].a==A[pc[j]].a)+(A[i].b==A[pc[j]].b);
if(cnt<2)pc[++lc]=i;
}
}
}
int main()
{
n=read();
scanf("%s",s+1);
solve();
int t=1;
for(int i=2;i<=n;i++)
{
if(s[i]==s[i-1])t++;
else t=1;
ans=max(ans,t);
}
printf("%d",ans);
}
对于每个 ( l , r , k ) (l,r,k) (l,r,k),新建一个点 p p p, p p p连向 k k k个点,然后再让这 k k k个数把 [ l , r ] [l,r] [l,r]分割成的若干个区间连向 p p p,线段树优化建图即可。
#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=400010;
const int inf=1000000000;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,s,m,deg[Maxn],a[Maxn],f[Maxn];
int id[Maxn];
struct Edge{int y,d,next;}e[Maxn*10];
int last[Maxn],len=0;
void ins(int x,int y,int d)
{
int t=++len;deg[y]++;
e[t].y=y;e[t].d=d;e[t].next=last[x];last[x]=t;
}
int tot=0;
struct Seg{int l,r,lc,rc;}tr[Maxn];
int T;
void build(int l,int r)
{
int x=++tot;
tr[x].l=l;tr[x].r=r;
if(l==r){id[l]=x;return;}
int mid=l+r>>1;
tr[x].lc=tot+1,build(l,mid);
tr[x].rc=tot+1,build(mid+1,r);
}
void insert(int x,int l,int r,int o,int d)
{
if(tr[x].l==l&&tr[x].r==r){ins(x,o,d);return;}
int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
if(r<=mid)insert(lc,l,r,o,d);
else if(l>mid)insert(rc,l,r,o,d);
else insert(lc,l,mid,o,d),insert(rc,mid+1,r,o,d);
}
int main()
{
memset(a,0,sizeof(a));
n=read(),s=read(),m=read();
build(1,n);
for(int i=1;i<=tot;i++)
if(tr[i].l!=tr[i].r)ins(tr[i].lc,i,0),ins(tr[i].rc,i,0);
for(int i=1;i<=s;i++)
{
int P=read(),D=read();
f[id[P]]=D;a[P]=D;
}
for(int i=1;i<=m;i++)
{
int l=read(),r=read(),k=read();
int t=++tot,lst=l;
while(k--)
{
int x=read();
ins(t,id[x],0);
if(lst<=x-1)insert(1,lst,x-1,t,1);
lst=x+1;
}
if(lst<=r)insert(1,lst,r,t,1);
}
for(int i=1;i<=tot;i++)if(!f[i])f[i]=1;
queue<int>q;
for(int i=1;i<=tot;i++)if(!deg[i])q.push(i);
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;deg[y]--;
f[y]=max(f[y],f[x]+e[i].d);
if(!deg[y])q.push(y);
}
}
for(int i=1;i<=tot;i++)if(deg[i])return puts("NIE"),0;
for(int i=1;i<=n;i++)if(f[id[i]]>inf)return puts("NIE"),0;
puts("TAK");
for(int i=1;i<=n;i++)printf("%d ",f[id[i]]);
}
直接枚举删哪一条边,设断边后两树的直径分别为 x x x、 y y y,由于边权为 1 1 1,所以最大直径为 x + y + 1 x+y+1 x+y+1,最小直径为 ⌈ x 2 ⌉ + ⌈ y 2 ⌉ + 1 \lceil{x\over2}\rceil+\lceil{y\over2}\rceil+1 ⌈2x⌉+⌈2y⌉+1。
至于求去掉某个子树后树的直径,可以DP,也可以无脑的线段树。
由于 n , a n,a n,a互质,数组中的数互不相同,设串第一位的数字为 x x x后就可以解不等式了。
#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=1000010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,a,b,p,m,ans=0;
char s[Maxn];
struct P{int l,r;P(int _l=0,int _r=0){l=_l,r=_r;}}pp[Maxn*3];int lp=0;
bool cmp(P a,P b)
{
if(a.l!=b.l)return a.l<b.l;
return a.r<b.r;
}
void w(int l,int r)
{
l=max(l,0),r=min(r,n-1);
if(l>r)return;
pp[++lp]=P(l,r);
}
int main()
{
n=read(),a=read(),b=read(),p=read(),m=read();
scanf("%s",s+1);
for(int i=1;i<=m;i++)
{
int t=(LL)a*(i-1)%n;
if(s[i]=='1')
{
w(0,p-t-1),w(n-t,p+n-t-1);
}
else
{
w(p-t,n-t-1),w(n+p-t,n-1);
}
}
for(int i=n-m+1;i<n;i++)
{
int t=((LL)a*i%n+b)%n;
w(t,t);
}
sort(pp+1,pp+1+lp,cmp);
int t=-1;
for(int i=1;i<=lp;i++)
{
int tmp=max(0,pp[i].l-1-t);
ans+=tmp;
t=max(t,pp[i].r);
}
ans+=max(0,n-1-t);
printf("%d\n",ans);
}