返回顶部

CSP16

image

这题,唯一坑点,子序列是不连续的
注意,子序列可以不连续,子串必须连续。
有一个很显然的暴力

点击查看代码
int dp[N][N],n,p[N],q[N];
int main()
{
	speed();
	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>p[i];
	for(int i=1;i<=n;i++)cin>>q[i];
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			if(q[j]%p[i]==0)
			{
				dp[i][j]=max(dp[i-1][j-1]+1,dp[i][j]);
			}
		}
	}
	cout<<dp[n][n]<<endl;
	return 0;
}

优化就是拿树状数组优化,顺便滚掉一位,注意啊倒序遍历,否则会更新错误,预处理处每个\(p_i\)对应的倍数
复杂度\(O(n\ln n\log n)\)

点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define lid (rt<<1)
#define rid (rt<<1|1)
#define endl '\n'
#define pb push_back
using namespace std;
const int N = 2e5+5;
int dp[N],n;
int p[N],q[N];
int posq[N],posp[N];
vector <int> pos[N];
int c[N];
int lowbit(int x){return x&-x;}
int query(int x)
{
	int ans=0;
	if(x<=0)return 0;
	while(x)
	{
		ans=max(ans,c[x]);
		x-=lowbit(x);
	}return ans;
}
void upd(int x,int val)
{
	if(x<=0)return ;
	while(x<=n)
	{
		c[x]=max(c[x],val);
		x+=lowbit(x);
	}
	return;
}
int main()
{
	speed();
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>p[i],posp[p[i]]=i;
	for(int i=1;i<=n;i++)cin>>q[i],posq[q[i]]=i;
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j+=i)
			if(posq[j])pos[i].pb(posq[j]);
		sort(pos[i].begin(),pos[i].end());
		reverse(pos[i].begin(),pos[i].end());
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		int ls=0;
		for(auto v:pos[p[i]])
		{
			int j=v;
			// cout<<v<<endl;
			dp[j]=query(j);
			dp[j]=max(query(j-1)+1,dp[j]);
			ans=max(ans,dp[j]);
			// cout<<dp[i][j]<<endl;
			upd(j,dp[j]);
		}
	}
	cout<<ans<<endl;
	return 0;
}

image

两种写法,
回滚莫队(常熟略大)

点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
#define pb push_back
using namespace std;
const int N = 2e5+5,mod=1e9+7;
int x[N],n,m;
int sq,B,st[500],ed[500],b[N],cnt[N],ans[N],cc[N];
struct qu
{
	int l,r,id,bl;
	bool operator < (const qu& A)const
	{
		if(bl==A.bl)return r<A.r;
		return l<A.l;
	}
}q[N];
void init()
{
	sq=sqrt(n);B=n/sq;
	for(int i=1;i<=B;i++)
	{
		st[i]=ed[i-1]+1;ed[i]=st[i]+sq-1;
		// cout<<st[i]<<" "<<ed[i]<<endl;
	}
	if(ed[B]<n)
	{
		B++;st[B]=ed[B-1]+1;ed[B]=n;
	}
}
int RES;
void add(int d)
{
	
	cnt[x[d]]++;
	// cout<<cnt[x[d]]<<endl;
	RES=max(RES,cnt[x[d]]);
	// cout<<res<<endl;
}
void del(int d)
{
	cnt[x[d]]--;
}
int force(int l,int r)
{
	memset(cc,0,sizeof cc);
	int ans=0;
	for(int i=l;i<=r;i++)cc[x[i]]++,ans=max(ans,cc[x[i]]);
	return ans;
}
int main()
{
	speed();
	// freopen("T2.in","r",stdin);
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	cin>>n>>m;
	
	for(int i=1;i<=n;i++)
	{
		cin>>x[i];b[i]=x[i];
	}
	sort(b+1,b+1+n);
	int res=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=n;i++)
	{
		x[i]=lower_bound(b+1,b+1+res,x[i])-b;
		// cout<<x[i]<<endl;
	}
	init();
	for(int i=1;i<=m;i++)
	{
		cin>>q[i].l>>q[i].r;q[i].id=i;
		q[i].bl=(q[i].l-1)/sq+1;
	}
	sort(q+1,q+1+m);int j=1;int L,R;
	// sq=1e9;
	for(int i=1;i<=B&&j<=m;i++)
	{
		L=ed[i]+1;R=ed[i];RES=0;
		memset(cnt,0,sizeof cnt);
		while(q[j].bl==i)
		{
			if(q[j].r-q[j].l<=sq)
			{
				ans[q[j].id]=force(q[j].l,q[j].r);
				j++;
				continue;
			}
			// cout<<L<<" "<<R<<" "<<q[j].l<<endl;
			L=ed[i]+1;
			while(R<q[j].r)
			{
				++R;
				add(R);
			}
			// cout<<res<<endl;
			ll tmp=RES;
			while(L>q[j].l)
			{
				L--;
				// cout<<L<<endl;
				add(L);
			}
			ans[q[j].id]=RES;
			// cout<<res<<endl;
			RES=tmp;
			while(L<=ed[i])del(L++);
			j++;
		}
	}
	for(int i=1;i<=m;i++)cout<<-ans[i]<<endl;
	return 0;
}

莫队

点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define lid (rt<<1)
#define rid (rt<<1|1)
#define endl '\n'
#define pb push_back
using namespace std;
const int N = 2e5+5;
int n,m,st[500],ed[500],sq,B,x[N],b[N],t[N],cnt[N],aa[N];
struct qu
{
	int l,r,id,bl;
	bool operator < (const qu& A)const
	{
		if(bl!=A.bl)return l<A.l;
		if(bl&1)return r<A.r;
		return r>A.r;
	}
}q[N];
void init()
{
	sq=sqrt(n);B=n/sq;
	for(int i=1;i<=B;i++)
	{
		st[i]=ed[i-1]+1;ed[i]=st[i]+sq-1;
	}
	if(ed[B]<n)
	{
		B++;st[B]=ed[B-1]+1;ed[B]=n;
	}
}
int ans;
inline void del(int x)
{
	if(ans==cnt[b[x]])ans--;
	t[cnt[b[x]]]--;
	if(t[cnt[b[x]]])ans=max(ans,cnt[b[x]]);
	cnt[b[x]]--;
	t[cnt[b[x]]]++;
	ans=max(ans,cnt[b[x]]);
}
inline void add(int x)
{
	t[cnt[b[x]]]--;
	cnt[b[x]]++;
	t[cnt[b[x]]]++;
	ans=max(ans,cnt[b[x]]);
}
int main()
{
	speed();
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>x[i];b[i]=x[i];
	}
	sort(x+1,x+1+n);
	int res=unique(x+1,x+1+n)-x-1;
	for(int i=1;i<=n;i++)
	{
		b[i]=lower_bound(x+1,x+1+res,b[i])-x;
		// cout<<x[i]<<endl;
	}init();
	for(int i=1;i<=m;i++)
	{
		cin>>q[i].l>>q[i].r;q[i].id=i;
		q[i].bl=(q[i].l-1)/sq+1;
	}
	sort(q+1,q+1+m);
	int l=1,r=0;
	for(int i=1;i<=m;i++)
	{
		while(l>q[i].l){add(--l);}
		while(l<q[i].l){del(l++);}
		while(r>q[i].r){del(r--);}
		while(r<q[i].r){add(++r);}
	
		aa[q[i].id]=ans;
	}
	for(int i=1;i<=m;i++)cout<<-aa[i]<<endl;
	return 0;
}

T3Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

image

简单路径有两个义项,可以指图G(V,E)中路径上的顶点都不相同的路径,还可以指Rn中的弧,亦称简单弧,是曲线弧概念的推广。
所以说,一个子树中的简单路径,是不包含自身,不重复走且是可以横跨两个儿子的路径
暴力的话,我们可以得到性质:一条路可以回文,必须每个字母出现次数为偶数,最多有一个奇数,这时候可以二进制压缩一下,当一个状态为全是\(0\)或只有一个\(1\)时才合法,期望得分\(50pts\)

点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define lid (rt<<1)
#define rid (rt<<1|1)
#define endl '\n'
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int N = 5e5+5;
int n,ans[N],fa[N];char val[N];
vector <int> edge[N];
map <int,int> len[N];
inline int get(int x)
{
	return 1<<(val[x]-'a');
}
inline void dfs(int u)
{
	len[u][0]=0;
	for(auto to:edge[u])
	{
		dfs(to);
		ans[u]=max(ans[to],ans[u]);
		for(auto v:len[to])
		{
			int zt=get(to)^v.first;
			if(len[u].find(zt)!=len[u].end())ans[u]=max(ans[u],len[u][zt]+v.second+1);
			if((zt^(zt&-zt))==0)ans[u]=max(ans[u],v.second+1);
			for(int j=0;j<22;j=-~j)
			{
				if(len[u].find(zt^(1<<j))!=len[u].end())
					ans[u]=max(ans[u],len[u][zt^(1<<j)]+v.second+1);
			}
		}
		for(auto v:len[to])
			len[u][get(to)^v.first]=max(len[u][get(to)^v.first],v.second+1);
	}
}
int main()
{
	speed();
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	cin>>n;
	for(int i=2;i<=n;i++)
	{
		cin>>fa[i]>>val[i];
		edge[fa[i]].pb(i);
	}
	dfs(1);
	for(int i=1;i<=n;i++)
		cout<<ans[i]<<" ";
	return 0;
}

正解用到了启发式合并,我们先想最原始的暴力,我们可以计算\(x\)\(1\)路径上的异或状态,想求\(u,v\)之间,直接异或即可\(lca\)以上部分可以抵消掉,只有一条路径合法当且仅当二进制表示下只有一个\(1\)或全是\(0\)的时候,如果\(u,v\)合法(要么\(dis_u=dis_v和dis_udis_v只有1个1\)),那么产生的贡献为\(dep_u+dep_v-2dep_{lca}\),我们可以用一个数组\(book\)存储每个状态对应的深度,遍历其所有子树,然后对于一个状态\(dsu\)与其\(⊕\)得到结果合法的状态是固定的\(23\)种,于是我们只要检查\(book\)内有没有合法的即可,\(book\)首先存的是重链里面的状态,然后再与其他儿子中的状态合并,这也是启发式合并的意义所在,这也就从\(O(23n^2)\)优化到了\(O(23n\log n)\)

点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define lid (rt<<1)
#define rid (rt<<1|1)
#define endl '\n'
#define pb push_back
using namespace std;
const int N = 1e6+5;
int fa[N],L[N],R[N],rk[N],dfstot,sz[N],son[N],dis[N],dep[N],n;char val[N];
int ans[N],book[1<<22];//book表示当前路径状态对应的深度
vector <int> edge[N];
inline void dfs1(int u)
{
	dep[u]=dep[fa[u]]+1;sz[u]=1;
	L[u]=++dfstot;rk[dfstot]=u;//欧拉序,作用是方便遍历所有儿子
	for(auto to:edge[u])
	{
		dis[to]=dis[u]^(1<<(val[to]-'a'));//计算路径状态
		dfs1(to);
		sz[u]+=sz[to];
		if(sz[to]>sz[son[u]])son[u]=to;
	}
	R[u]=dfstot;
}
inline void dfs2(int u,bool heavy)
{
	for(auto to:edge[u])
	{
		if(to==son[u])continue;
		dfs2(to,0);
		ans[u]=max(ans[u],ans[to]);
	}
	if(son[u])dfs2(son[u],1),ans[u]=max(ans[u],ans[son[u]]);
	if(book[dis[u]])ans[u]=max(ans[u],book[dis[u]]-dep[u]);//更新答案
	for(int j=0;j<22;j++)
		if(book[dis[u]^(1<<j)])ans[u]=max(ans[u],book[dis[u]^(1<<j)]-dep[u]);//异或判断状态合法
	book[dis[u]]=max(book[dis[u]],dep[u]);//更新()别忘了
	for(auto to:edge[u])
	{
		if(to==son[u])continue;
		for(int j=L[to];j<=R[to];j++)
		{
			int id=rk[j];
			if(book[dis[id]])ans[u]=max(ans[u],book[dis[id]]+dep[id]-2*dep[u]);
			for(int k=0;k<22;k++)
				if(book[dis[id]^(1<<k)])ans[u]=max(ans[u],book[dis[id]^(1<<k)]+dep[id]-2*dep[u]);
		}
		for(int j=L[to];j<=R[to];j++)book[dis[rk[j]]]=max(dep[rk[j]],book[dis[rk[j]]]);//计算完当前儿子分支,就可更新为下一个儿子做准备
	}
	if(!heavy)for(int j=L[u];j<=R[u];j++)book[dis[rk[j]]]=0;
}
int main()
{
	speed();
	// freopen("T3.in","r",stdin);
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	cin>>n;
	for(int i=2;i<=n;i++)
	{
		cin>>fa[i]>>val[i];
		edge[fa[i]].pb(i);
	}
	dfs1(1);
	dfs2(1,1);
	for(int i=1;i<=n;i++)	
		cout<<ans[i]<<" ";
	return 0;
}

T4[AGC049D] Convex Sequence

image

暴力

点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define lid (rt<<1)
#define rid (rt<<1|1)
#define endl '\n'
#define pb push_back
using namespace std;
const int N = 1e5+5,mod=1e9+7;
ll ans=0;int n,m;
unordered_map <int,unordered_map<int,unordered_map<int,unordered_map<int,int>>>> f;
inline ll dfs(int now,ll cha,int sum,int ai)
{
	if(sum>m)return 0;
	if(now==n+1)
	{
		// for(int i=1;i<=n;i++)cout<<p[i]<<" ";
		// cout<<endl;
		return (m==sum);
	}
	if(f[now][cha][sum][ai])return f[now][cha][sum][ai];
	ll ans=0;
	for(int i=0;i<=m-sum;i++)
	{
		if(i-ai>=cha)
		{
			ans=(ans+dfs(now+1,i-ai,sum+i,i))%mod;
		}	
	}
	return f[now][cha][sum][ai]=ans;
}
int main()
{
	speed();
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	cin>>n>>m;
	ll ans=0;
	for(int i=0;i<=m;i++)
	{
		ans=(ans+dfs(2,-1e9,i,i))%mod;
	}
	cout<<ans;
	return 0;
}

怎么说呢挂分变少了,有进步

posted @ 2024-08-08 20:28  wlesq  阅读(28)  评论(1编辑  收藏  举报