修仙录 3.27

emmmmmmmm


jzoj 6090 圆

https://jzoj.net/senior/#main/show/6090
dp。
能分成三个上升子序列的话,说明最长下降子序列不能超过3.。。。
考虑把数从小到大依次加入序列中
那么我们目前要加入的那个数一定是序列中最大的
发现如果在某个位置右边下降子序列长度为3,那么这个位置左边一定不能加数
那我们就想办法维护这种位置
但是不是很好搞。
主要是要找到dp转移出现区别的临界点
我们看看右边可以加入的部分
考虑这样一个位置x:他的右边一直到序列结束都是单增,左边的第一个位置比他大
其实就是右端点的极长下降连续序列的最后一个位置
在这个位置两边插入新数产生的效果并不一样:

  • 1.如果插左边,那么新数,x-1处和x处的三个数构成长度为3的下降子序列,那么新插入的位置左边以后都不可以再插
  • 2.如果插右边,那么新数切断了原来的极长下降连续序列,即x变为新插入的位置

这样看来dp就可以写了,只需要维护极长下降连续序列的长度,和x左边还可以插入新数区间的长度
用前缀和优化就好
当然你高兴的话还可以打表。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
const int MAXN=505;

int n,mod,o;
LL f[2][MAXN][MAXN],s1[2][MAXN][MAXN],s2[2][MAXN][MAXN],ans;

void add(LL &a,LL b){
	a+=b;
	if(a>=mod) a-=mod;
}

int main(){
	freopen("yuan.in","r",stdin);
	freopen("yuan.out","w",stdout);
	cin>>n>>mod;
	f[o][1][0]=s2[o][1][1]=1;
	for(int i=2;i<=n;i++){
		o^=1;
		for(int j=0;j<=i;j++)for(int k=0;k<=i;k++) f[o][j][k]=s1[o][j][k]=s2[o][j][k]=0;
		for(int j=1;j<=i;j++)for(int k=0;j+k<=i;k++){
			if(!k&&j!=i) continue;
			add(f[o][j][k],f[o^1][j-1][k]);
			add(f[o][j][k],s1[o^1][j][k]);
			add(f[o][j][k],s2[o^1][j+k-1][j]);
			add(s1[o][j][k],f[o][j][k]);
			add(s2[o][j+k][j],f[o][j][k]);
		}
		for(int j=1;j<=i;j++)for(int k=i;k>=1;k--){
			add(s1[o][j][k],s1[o][j][k+1]);
			add(s2[o][j][k],s2[o][j][k+1]);
		}
	}
	for(int i=1;i<=n;i++)for(int j=0;i+j<=n;j++) add(ans,f[o][i][j]);
	cout<<ans;
	return 0;
}

jzoj 6089 胖

https://jzoj.net/senior/#main/show/6089
正解什么的果然可怕:https://jzoj.net/senior/index.php/main/download/6089/FBCHEF_sol.pdf/0/solution_path
我肯定不会打呀!
暴力出奇迹。
直接和昨天第二题一样每层开一个线段树维护(有多少点开多大)
暴力改,树状数组查询答案
dfn线段树真好。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN=1e5+5;
const int MAXM=MAXN*6;
const int inf=1e9;

int n,w[MAXN],q;
int head[MAXN],to[MAXN*2],next[MAXN*2],cnt;
int dfn[MAXN],las[MAXN],tot,dep[MAXN],fa[MAXN],pos[MAXN];
struct Tree{
	int c[MAXN];
	void add(int x,int v){
		for(x;x<=n;x+=x&-x) c[x]+=v;
	}
	int ask(int x){
		int ans=0;
		for(x;x;x-=x&-x) ans+=c[x];
		return ans;
	}
}tr;

struct node{
	int v,p;
	node(){v=inf,p=0;}
	node(int v,int p):v(v),p(p){}
}mn[MAXM];

int lc[MAXM],rc[MAXM],lx[MAXM],rx[MAXM],nd,laz[MAXM];
struct tree{
	vector<int>a;
	int rt;
	void init(){a.push_back(0);}
	void ins(int x){a.push_back(x);}
	void up(int p){
		lx[p]=lx[lc[p]],rx[p]=rx[rc[p]];
		mn[p]= mn[lc[p]].v<mn[rc[p]].v ? mn[lc[p]] : mn[rc[p]];
	}
	void Plus(int p,int v){
		if(mn[p].v==inf) return ;
		mn[p].v+=v,laz[p]+=v;
	}
	void down(int p){
		if(!laz[p]) return ;
		Plus(lc[p],laz[p]),Plus(rc[p],laz[p]);
		laz[p]=0;
	}
	void add(int p,int L,int R,int v){
		if(mn[p].v==inf) return ;
		if(L<=lx[p]&&rx[p]<=R){Plus(p,v);return ;}
		down(p);
		if(L<=rx[lc[p]]) add(lc[p],L,R,v);
		if(lx[rc[p]]<=R) add(rc[p],L,R,v);
		up(p);
	}
	void build(int &p,int l,int r){
		p=++cnt;
		if(l==r){
			lx[p]=rx[p]=a[l],mn[p]=node(w[pos[a[l]]],a[l]);
			return ;
		}
		int mid=l+r>>1;
		build(lc[p],l,mid);
		build(rc[p],mid+1,r);
		up(p);
	}
	void change(int p,int x){
		if(lx[p]==rx[p]){mn[p].v=inf;return ;}
		down(p);
		if(x<=rx[lc[p]]) change(lc[p],x);
		else change(rc[p],x);
		up(p);
	}
	void keep(){
		while(mn[rt].v<=0){
			tr.add(mn[rt].p,1);
			change(rt,mn[rt].p);
		}
	}
	void pre(){
		if(a.size()<=1) return ;
		build(rt,1,a.size()-1);keep();
	}
}t[MAXN];

void add(int u,int v){
	next[++cnt]=head[u],to[cnt]=v,head[u]=cnt;
	next[++cnt]=head[v],to[cnt]=u,head[v]=cnt;
}

void dfs(int x,int F){
	dfn[x]=++tot,pos[tot]=x,dep[x]=dep[F]+1,fa[x]=F;
	t[dep[x]].ins(dfn[x]);
	for(int i=head[x];i;i=next[i]){
		int y=to[i];
		if(y==F) continue;
		dfs(y,x);
	}
	las[x]=tot;
}

void turn(int l,int r,int v,int d){
	for(v;v;v/=2,d++){
		if(t[d].a.size()==1||r<lx[t[d].rt]||l>rx[t[d].rt]) continue;
		t[d].add(t[d].rt,l,r,-v);
		t[d].keep();
	}
}

void insert(int x,int v){
	turn(dfn[x],las[x],v,dep[x]);v/=2;
	for(;v;x=fa[x],v/=2){
		turn(dfn[fa[x]],dfn[x]-1,v,dep[fa[x]]);
		if(las[x]!=las[fa[x]]) turn(las[x]+1,las[fa[x]],v,dep[fa[x]]);
	}
}

int main(){
	freopen("pang.in","r",stdin);
	freopen("pang.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",&w[i]),t[i].init();
	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),add(u,v);
	dfs(1,0);
	for(int i=1;i<=n;i++) t[i].pre(); 
	cin>>q;
	while(q--){
		int op;scanf("%d",&op);
		if(op==1){
			int A,X;scanf("%d%d",&A,&X);
			insert(A,X);
		}
		if(op==2){
			int A;scanf("%d",&A);
			printf("%d\n",tr.ask(las[A])-tr.ask(dfn[A]-1));
		}
	}
	return 0;
}

T1后面一扯到数学就全程懵逼。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值