wxy 3.10 牛客练习赛56 重现
v>
wxy 3.10 牛客练习赛56 重现
4841
B
考虑一条边,从左边过去和从右边过来经过的分别为两个子树的大小和人数乘积的和的两倍,想到有点
类似于换根DP和树形DP,但是LL要和lld相匹配要注意。
C
朴素做法是n^2*k的,考虑到转移的时候是前缀和,前缀和==区间和,想到树状数组,f [ q ] [ j ] ,到q这
个数长度为j的严格递增子序个数。首先预处理一下a数组,用a[i]是第几大的数代替a[i]本身,开10棵树
状数组,对每一个数cpy=bin[j-1].sum(a[i]-1)加到f[a[i]] [j]的值,现在的a[i]是第几大,那么在a[i]之前的
都是比他小的那么是可以转移的,并在之后bit[j].add(a[i],cpy)树状数组和f数组同步更新。 最后遍历一
遍f[i] [k]累加,即为以每个数结尾的长度为k的子序列的个数。f[a[i]] [k]会重复。
# include <bits/stdc++.h>
using namespace std;
const int MAXN=5e5+100;
const int mod=998244353;
int a[MAXN],b[MAXN],cpy[20],f[MAXN][20];
int lowbit(int x){ return x&(-x); }
int n,k;
struct Bit{
int c[MAXN];
void add(int x,int y) //x更新位置 y为更新后的数
{
for(int i=x;i<=n;i+=lowbit(i))
c[i]=(c[i]+y)%mod;
}
int sum(int x) //1-x 求和
{
int ans=0;
for(int i=x;i;i-=lowbit(i))
ans=(ans+c[i])%mod;
return ans;
}
}bit[20];
int main()
{
for (int i=0;i<=10;i++)
memset(bit[i].c,0,sizeof(bit[i].c));
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
int len=unique(b+1,b+n+1)-(b+1);
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+len+1,a[i])-b;
}D
可以考虑枚举在那个点取到最小值,(预处理,开始覆盖每个区间左端点的那个点的是哪几个区间,到
那个位置区间右端点结束覆盖的是哪几个区间),用线段树维护区间最值,差即为答案的一个可能。查
到线段树的板子有点问题,又一遍深入理解了一下。
for(int i=1;i<=n;i++){
bit[1].add(a[i],1);
for(int j=2;j<=k;j++){
cpy[j]=bit[j-1].sum(a[i]-1);
f[a[i]][j]=(f[a[i]][j]+cpy[j])%mod;
bit[j].add(a[i],cpy[j]);
}
}
int ans=0;
for(int i=1;i<=n;i++) ans=(ans+f[i][k])%mod;
printf("%d\n",ans);
return 0;
}
# include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=2e5+100;
struct S_tree{
struct Node{
int l,r;
LL maxx,minn,add_lazy;
}a[MAXN<<2];
inline void Push_Up(int k) //向上更新
{
if(a[k].l==a[k].r) return ;
a[k].maxx=max(a[k<<1].maxx,a[k<<1|1].maxx);
a[k].minn=min(a[k<<1].minn,a[k<<1|1].minn);
}
inline void Build(int k,int l,int r) //建树
{
a[k].l=l; a[k].r=r;
a[k].add_lazy=0;
if(l==r){ //到达叶子节点
LL arr;
scanf("%lld",&arr);
a[k].maxx=a[k].minn=arr;
return ;
}
int mid=(l+r)>>1;
Build(k<<1,l,mid);
Build(k<<1|1,mid+1,r);
Push_Up(k);
}
inline void Push_Down(int k) //向下更新
{
if(a[k].add_lazy)
{
a[k<<1].add_lazy+=a[k].add_lazy;a[k<<1|1].add_lazy+=a[k].add_lazy;
a[k<<1].maxx+=a[k].add_lazy;
a[k<<1|1].maxx+=a[k].add_lazy;
a[k<<1].minn+=a[k].add_lazy;
a[k<<1|1].minn+=a[k].add_lazy;
a[k].add_lazy=0;
}
}
inline void add_change(int k,int l,int r,LL val)//add
{
if(a[k].l==l&&a[k].r==r){
a[k].add_lazy+=val;
a[k].maxx+=val; a[k].minn+=val;
return ;
}
Push_Down(k);
int mid=(a[k].l+a[k].r)>>1;
if(r<=mid) add_change(k<<1,l,r,val);
else if(l>mid) add_change(k<<1|1,l,r,val);
else { add_change(k<<1,l,mid,val); add_change(k<<1|1,mid+1,r,val); }
Push_Up(k);
}
inline LL max_q(int k,int l,int r) //max
{
if(a[k].l==l&&a[k].r==r) return a[k].maxx;
Push_Down(k);
int mid=(a[k].l+a[k].r)>>1;
if(r<=mid) return max_q(k<<1,l,r);
else if(l>mid) return max_q(k<<1|1,l,r);
else return max(max_q(k<<1,l,mid),max_q(k<<1|1,mid+1,r));
}
inline LL min_q(int k,int l,int r) //min
{
if(a[k].l==l&&a[k].r==r) return a[k].minn;
Push_Down(k);
int mid=(a[k].l+a[k].r)>>1;
if(r<=mid) return min_q(k<<1,l,r);
else if(l>mid) return min_q(k<<1|1,l,r);
else return min(min_q(k<<1,l,mid),min_q(k<<1|1,mid+1,r));
}
} S;
int h[MAXN];
struct Ode{
int l,r; LL w;
}node[MAXN];
int cmp(Ode a,Ode b)
{
if(a.l==b.l) return a.r<b.r;
return a.l<b.l;
}
vector<int> L[MAXN],R[MAXN];
int main()
{E
割边,求树的带权直径
F
马拉车,NTT
int n,m; scanf("%d%d",&n,&m);
S.Build(1,1,n);
for(int i=1;i<=m;i++){
scanf("%d%d%lld",&node[i].l,&node[i].r,&node[i].w);
}
sort(node+1,node+m+1,cmp);
for(int i=1;i<=m;i++){
L[node[i].l].push_back(i);//被l包含的是哪几个区间
R[node[i].r+1].push_back(i);//被r筛下的是哪几个区间
}
LL ans=0;
for(int i=1;i<=n;i++){
for(int j=0;j<L[i].size();j++){
S.add_change(1,node[L[i][j]].l,node[L[i][j]].r,-node[L[i][j]].w);
}
for(int j=0;j<R[i].size();j++){
S.add_change(1,node[R[i][j]].l,node[R[i][j]].r,node[R[i][j]].w);
}
LL Min,Max;
Min=S.min_q(1,i,i);
Max=S.max_q(1,1,n);
//cout<<Max<<" "<<Min<<endl;
ans=max(Max-Min,ans);
}
printf("%lld\n",ans);
return 0;
}
/*
3 3
7 -2 -10
1 3 4
3 3 4
1 2 8
*/
向wjmzbmr学习,acm本就是逆天而行。