2019-7-2 杂题选讲

2019-7-2 杂题选讲

T1 Lynyrd Skynyrd

以前做过,预处理好从第 i i i位开始跳,需要跳完一个循环后落到了什么位置,把边界设置好 i n f inf inf就能倍增开跳,然后再把这个预处理数组做一个后缀取最小值,每次询问只需要 O ( 1 ) O(1) O(1)判断是否 g [ l ] &lt; = r g[l]&lt;=r g[l]<=r即可

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
const int LOGN=20;
int n,m,q,p[N],a[N],l,r,tot;
int nxt[N],pos[N],f[N][LOGN],g[N];
int er[N];
int ans[N];
int read()
{
	int x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}
int main()
{
	er[0]=1;
	for (int i=1;i<LOGN;i++) er[i]=er[i-1]*2;
	n=read(),m=read(),q=read();
	for (int i=1;i<=n;i++) p[i]=read();
	for (int i=1;i<=m;i++) a[i]=read();
	for (int i=1;i<=n;i++) nxt[p[i]]=p[i%n+1];
	for (int i=1;i<=n;i++) pos[i]=m+1;
	for (int i=0;i<N;i++)
		for (int j=0;j<LOGN;j++) f[i][j]=m+1;
	for (int i=m;i>=1;i--)
	{
		f[i][0]=pos[nxt[a[i]]];
		for (int j=1;j<LOGN;j++) f[i][j]=f[f[i][j-1]][j-1];
		pos[a[i]]=i;
	}
	g[m+1]=m+1;
	for (int i=m;i>=1;i--)
	{
		int l=i,rest=n-1;
		while (rest)
		{
			int jump=log2(rest);
			l=f[l][jump];
			rest-=er[jump];
		}
		g[i]=min(g[i+1],l);
	}
	while (q--)
	{
		l=read(),r=read();
		if (g[l]<=r) ans[++ans[0]]=1;
		else ans[++ans[0]]=0;
	}
	for (int i=1;i<=ans[0];i++) printf("%d",ans[i]);
	return 0;
}

好题T2 Tree Generator™

回归树的括号序列的定义:向深层递归是左括号 ( ( (,向上层回溯是右括号 ) ) )
那么就可以发现:
1,形如 ( ( ( ( ( ) ) ) ) ) ((((())))) ((((()))))的一段括号序列的意义是,从某个点向深层走并原路返回
2,形如 ) ) ) ) ) ( ( ( ( ( )))))((((( )))))(((((的一段括号序列的意义是,从某个点向上回溯至某个位置并向下搜索另一个子树,这样走出来的是一条路径
那我们需要的直径明显是形如2的一条最长路径,但又因为中间可能遍历了其他子树,但是中间这些括号都能对消掉,于是我们可以想到直径在括号序列上的意义是选择一段括号序列,将可以匹配对消的去掉后的最长长度
这个东西好像可以在线段树上合并,考虑一下怎么合并
记录一下信息:
l n , r n , l x , r x , s u m ln,rn,lx,rx,sum ln,rn,lx,rx,sum分别表示区间前后缀最小最大和以及区间和
a n s , l a n s , r a n s ans,lans,rans ans,lans,rans分别表示区间答案,区间中必须取左端点的最优答案,必须取右端点的最优答案
最后的难点就是合并信息
拿lans举个例子:
t [ p ] . l a n s = m a x ( t [ l s ] . l a n s , m a x ( − t [ l s ] . l n + t [ l s ] . r x + t [ r s ] . l x , − t [ l s ] . s u m + t [ r s ] . l a n s ) ) ; t[p].lans=max(t[ls].lans,max(-t[ls].ln+t[ls].rx+t[rs].lx,-t[ls].sum+t[rs].lans)); t[p].lans=max(t[ls].lans,max(t[ls].ln+t[ls].rx+t[rs].lx,t[ls].sum+t[rs].lans));
第一项是继承左儿子答案
第二项是左区间左边有一段))左区间右端和有区间左端有一段((
第三项是强制去左区间并继承右儿子的答案

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,q,tot;
int ans[N],tpp=0;
char s[N];
struct data
{
    int sum,ln,lx,rn,rx,ans,lans,rans;
}t[N<<3];
void Pushup(int p,int ls,int rs)
{
    t[p].sum=t[ls].sum+t[rs].sum;
    t[p].ln=min(t[ls].ln,t[ls].sum+t[rs].ln);
    t[p].lx=max(t[ls].lx,t[ls].sum+t[rs].lx);
    t[p].rn=min(t[rs].rn,t[rs].sum+t[ls].rn);
    t[p].rx=max(t[rs].rx,t[rs].sum+t[ls].rx);
    t[p].lans=max(t[ls].lans,max(-t[ls].ln+t[ls].rx+t[rs].lx,-t[ls].sum+t[rs].lans));
    t[p].rans=max(t[rs].rans,max(t[rs].rx-t[rs].ln-t[ls].rn,t[rs].sum+t[ls].rans));
    t[p].ans=max(max(t[ls].ans,t[rs].ans),max(t[ls].rans+t[rs].lx,t[rs].lans-t[ls].rn));
    return;
}
void Build(int p,int l,int r)
{
    if (l==r) 
    {
        int val=(s[l]=='(')?1:-1;
        t[p]=(data){val,min(0,val),max(0,val),min(0,val),max(0,val),1,1,1};
        return;
    }
    int ls=p<<1,rs=p<<1|1,mid=(l+r)>>1;
    Build(ls,l,mid);
    Build(rs,mid+1,r);
    Pushup(p,ls,rs);
    return;
}
void Modify(int p,int l,int r,int k,int v)
{
    if (l==r)
    {
        t[p]=(data){v,min(v,0),max(v,0),min(v,0),max(v,0),1,1,1};
        return;
    }
    int ls=p<<1,rs=p<<1|1,mid=(l+r)>>1;
    if (k<=mid) Modify(ls,l,mid,k,v);
    else Modify(rs,mid+1,r,k,v);
    Pushup(p,ls,rs);
    return;
}
int main()
{
    scanf("%d%d",&n,&q);
    tot=(n-1)<<1;
    scanf("%s",s+1);
    Build(1,1,tot);
    ans[++tpp]=t[1].ans;
    while (q--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        Modify(1,1,tot,l,(s[r]=='(')?1:-1);
        Modify(1,1,tot,r,(s[l]=='(')?1:-1);
        swap(s[l],s[r]);
        ans[++tpp]=t[1].ans;
    }
    for (int i=1;i<=tpp;i++) printf("%d\n",ans[i]);
    return 0;
}

T3 Three Religions

d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]为三个小串分别匹配到 i , j , k i,j,k i,j,k位需要的最短的大串前缀,加入一个字符多填一些状态,删掉一个字符撤回一些状态,所有边界设置 n + 1 n+1 n+1

d p [ i ] [ j ] [ k ] = m i n ( d p [ n x t [ d p [ i − 1 [ j ] [ k ] ] ] . . . ) dp[i][j][k]=min(dp[nxt[dp[i-1[j][k]]]...) dp[i][j][k]=min(dp[nxt[dp[i1[j][k]]]...)

#include<bits/stdc++.h>
using namespace std;
const int N=260;
const int S=1e5+5;
const int inf=1e9+7;
int n,q,tmp;
int nxt[S][N],pos[30],dp[N][N][N];
int s1[N],s2[N],s3[N],l1=0,l2=0,l3=0;
char s[S],opt[10],ch[10];
void Init()
{
	for (int i=0;i<N;i++)
		for (int j=0;j<N;j++)
			for (int k=0;k<N;k++) dp[i][j][k]=n+1;
	for (int i=0;i<26;i++) pos[i]=n+1;
	for (int i=0;i<=n+1;i++)
		for (int j=0;j<26;j++) nxt[i][j]=n+1;
	dp[0][0][0]=0;
	for (int i=n;i>=0;i--)
	{
		for (int j=0;j<26;j++) nxt[i][j]=pos[j];
		pos[s[i]-'a']=i;
	}
	return;
} 
void DO(int i,int j,int k)
{
	if (i>0) dp[i][j][k]=min(dp[i][j][k],nxt[dp[i-1][j][k]][s1[i]]);
	if (j>0) dp[i][j][k]=min(dp[i][j][k],nxt[dp[i][j-1][k]][s2[j]]);
	if (k>0) dp[i][j][k]=min(dp[i][j][k],nxt[dp[i][j][k-1]][s3[k]]); 
//	printf("dp[%d][%d][%d] = %d\n",i,j,k,dp[i][j][k]);
	return;
}
int main()
{
	scanf("%d%d",&n,&q);
	scanf("%s",s+1);
	Init();
	while (q--)
	{
		scanf("%s",opt+1);
		scanf("%d",&tmp);
		switch (opt[1])
		{
			case '+':
				scanf("%s",ch+1);
				if (tmp==1)
				{
					s1[++l1]=ch[1]-'a';
					int i=l1;
					for (int j=0;j<=l2;j++)
						for (int k=0;k<=l3;k++) DO(i,j,k);
				}
				else if (tmp==2)
				{
					s2[++l2]=ch[1]-'a';
					int j=l2;
					for (int i=0;i<=l1;i++)
						for (int k=0;k<=l3;k++) DO(i,j,k);
				}
				else
				{
					s3[++l3]=ch[1]-'a';
					int k=l3;
					for (int i=0;i<=l1;i++)
						for (int j=0;j<=l2;j++) DO(i,j,k);
				}
				break;
			case '-':
				if (tmp==1)
				{
					int i=l1;
					for (int j=0;j<=l2;j++)
						for (int k=0;k<=l3;k++) dp[i][j][k]=n+1;
					l1--;
				}
				else if (tmp==2)
				{
					int j=l2;
					for (int i=0;i<=l1;i++)
						for (int k=0;k<=l3;k++) dp[i][j][k]=n+1;
					l2--;
				}
				else
				{
					int k=l3;
					for (int i=0;i<=l1;i++)
						for (int j=0;j<=l2;j++) dp[i][j][k]=n+1;
					l3--;
				}
				break;
		} 
		if (dp[l1][l2][l3]<=n) printf("YES\n");
		else printf("NO\n");
	}
/*	for (int i=0;i<=n;i++)
	{
		for (int j=0;j<=5;j++) printf("%d ",nxt[i][j]);
		printf("\n");
	}*/
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值