zoukankan      html  css  js  c++  java
  • 【解题报告】洛谷P5658 括号树

    【解题报告】洛谷P5658 括号树

    题目链接

    https://www.luogu.com.cn/problem/P5658

    思路

    看到这道题目,我们有必要了解一下括号序列

    然后不会打正解?

    好,打部分分

    • (f_i=i-1) 的部分

      • (n le 200)

        这一部分的分数我们可以直接打暴力,然后三个循环 (O(n^3)) 再加上一个 (O(n)) 的判断被嵌套在这三个循环里面,总共是 (O(n^4)) ,可以过掉这一部分的数据

        期望得分 20pts

        #include <iostream>
        #include <cstdio>
        #include <algorithm>
        #include <cstring>
        #include <string>
        using namespace std;
        const int maxn=500005;
        int n,fa[maxn],ans;
        bool flag=true;
        char s[maxn];
        struct edge{
        	int e,next;
        }ed[maxn<<1];
        int en,first[maxn];
        void add_edge(int s,int e)
        {
        	en++;
        	ed[en].next=first[s];
        	first[s]=en;
        	ed[en].e=e;
        }
        bool check(int l,int r)
        {
        	int cnt=0;
        	for(int i=l;i<=r;i++)
        	{
        		if(s[i]=='(') cnt++;
        		else if(s[i]==')') cnt--;
        		if(cnt<0) return false;
        	}
        	if(cnt==0) return true;
        	return false;
        }
        int main()
        {
        	cin>>n;
        	scanf("%s",s+1);
        	for(int i=1;i<=n-1;i++)
        	{
        		int x;
        		cin>>x;
        		fa[i+1]=x;
        		if(fa[i+1]!=i) flag=false;
        		add_edge(i+1,x);
        		add_edge(x,i+1);
        	}
        	if(flag)
        	{
        		for(int i=1;i<=n;i++)
        		{
        			int cnt=0;
        			for(int l=1;l<=i;l++)
        			{
        				for(int r=l+1;r<=i;r++)
        				{
        					if(check(l,r))
        						cnt++;
        				}
        			}
        			ans=(ans^(cnt*i));
        		}
        		cout<<ans<<'
        ';
        		return 0;
        	}
        	return 0;
        }
        
      • (n le 10^5) 的部分

        我们可以发现,实际上我们的瓶颈在于我们有一个 (n^3) 的美剧左端点,枚举右端点和一次判断,我们要完成 (10^5) 的数据,至少要达到 (O(nlogn)) 的时间复杂度,所以我们要继续优化

        根据大佬的提示,我们可以知道下面一段话

        我们发现,一个后括号如果能匹配一个前括号,假设这个前括号的前1位同样有一个已经匹配了的后括号,那么我们势必可以把当前的匹配和之前的匹配序列合并,当前的这个后括号的贡献值,其实就等于前面那个后括号的贡献值+1!

        然后我们就可以按照这个写出代码,这个结论要用手模一下

        期望得分 55pts

        #include <iostream>
        #include <cstdio>
        #include <algorithm>
        #include <cstring>
        #include <string>
        #define int long long
        using namespace std;
        const int maxn=500005;
        int n,fa[maxn],ans;
        int st[maxn],don[maxn],sum[maxn],size;
        bool flag=true;
        char s[maxn];
        struct edge{
        	int e,next;
        }ed[maxn<<1];
        int en,first[maxn];
        void add_edge(int s,int e)
        {
        	en++;
        	ed[en].next=first[s];
        	first[s]=en;
        	ed[en].e=e;
        }
        bool check(int l,int r)
        {
        	int cnt=0;
        	for(int i=l;i<=r;i++)
        	{
        		if(s[i]=='(') cnt++;
        		else if(s[i]==')') cnt--;
        		if(cnt<0) return false;
        	}
        	if(cnt==0) return true;
        	return false;
        }
        signed main()
        {
        	cin>>n;
        	scanf("%s",s+1);
        	for(int i=1;i<=n-1;i++)
        	{
        		int x;
        		cin>>x;
        		fa[i+1]=x;
        		if(fa[i+1]!=i) flag=false;
        		add_edge(i+1,x);
        		add_edge(x,i+1);
        	}
        	if(flag)
        	{
        		for(int i=1;i<=n;i++)
        		{
        			if(s[i]==')')
        			{
        				if(size==0) continue;
        				int t=st[size];
        				don[i]=don[t-1]+1;
        				size--;
        			}
        			else if(s[i]=='(')
        			st[++size]=i;
        		}
        		for(int i=1;i<=n;i++)
        			sum[i]=sum[i-1]+don[i];
        		for(int i=1;i<=n;i++)
        		ans=(ans^(i*sum[i]));
        		cout<<ans<<'
        ';
        		return 0;
        	}
        	return 0;
        }
        
    • 无特殊性质

      这种情况,我们要做的是把序列上的问题转化到树上,每个结点到根的简单路径有且仅有一条,所以如果单单是对于这一条链的话,策略跟上面一样,但是我们的前一个结点就变了,上一个左括号的,也就是上面的代码中的 (t) 我们要改成 (fa[t]) 这样,然后计算每个结点对应的合法序列的个数也要改变

      那我们压进栈里面的数字怎么办啊

      我们设置了一个中间值,记录上一个左括号的位置,如果这个中间值为 (0) 的话,说明这是一个左括号,那么在栈里面加入它,如果不是 (0) 的话,判断一下栈的大小是否为 (0) ,如果不是 (0) 的话,我们就可以发现一个事情,在之前的递归中,我们一定压入了一个数,但是这个数会影响后面的答案,所以我们将这个值弹出,相当于回溯一下子

      期望得分100pts

      #include <iostream>
      #include <cstdio>
      #include <algorithm>
      #include <cstring>
      #include <string>
      #define int long long
      using namespace std;
      const int maxn=500005;
      int n,fa[maxn],ans;
      int st[maxn],don[maxn],sum[maxn],size;
      bool flag=true;
      char s[maxn];
      struct edge{
      	int e,next;
      }ed[maxn<<1];
      int en,first[maxn];
      void add_edge(int s,int e)
      {
      	en++;
      	ed[en].next=first[s];
      	first[s]=en;
      	ed[en].e=e;
      }
      
      
      void dfs(int x)
      {
      	int tmp=0;
      	if(s[x]==')')
      	{
      		if(size!=0)
      		{
      			tmp=st[size];
      			don[x]=don[fa[tmp]]+1;
      			size--;
      		}
      	}
      	else if(s[x]=='(')
      	st[++size]=x;
      	sum[x]=sum[fa[x]]+don[x];
      	for(int i=first[x];i;i=ed[i].next)
      	{
      		int e=ed[i].e;
      		dfs(e);
      	}
      	if(tmp!=0)
      	st[++size]=tmp;
      	else if(size)
      	size--;
      }
      
      bool check(int l,int r)
      {
      	int cnt=0;
      	for(int i=l;i<=r;i++)
      	{
      		if(s[i]=='(') cnt++;
      		else if(s[i]==')') cnt--;
      		if(cnt<0) return false;
      	}
      	if(cnt==0) return true;
      	return false;
      }
      signed main()
      {
      	cin>>n;
      	scanf("%s",s+1);
      	for(int i=2;i<=n;i++)
      	{
      		int x;
      		cin>>x;
      		add_edge(x,i);
      		fa[i]=x;
      	}
      	dfs(1);
      	for(int i=1;i<=n;i++)
      	ans=(ans^(i*sum[i]));
      	cout<<ans<<'
      ';
      	return 0;
      }
      
    本博文为wweiyi原创,若想转载请联系作者,qq:2844938982
  • 相关阅读:
    BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )
    BZOJ 2134: 单选错位( 期望 )
    BZOJ 1030: [JSOI2007]文本生成器( AC自动机 + dp )
    BZOJ 2599: [IOI2011]Race( 点分治 )
    BZOJ 3238: [Ahoi2013]差异( 后缀数组 + 单调栈 )
    ZOJ3732 Graph Reconstruction Havel-Hakimi定理
    HDU5653 Bomber Man wants to bomb an Array 简单DP
    HDU 5651 xiaoxin juju needs help 水题一发
    HDU 5652 India and China Origins 并查集
    HDU4725 The Shortest Path in Nya Graph dij
  • 原文地址:https://www.cnblogs.com/wweiyi2004/p/15394605.html
Copyright © 2011-2022 走看看