P7324 [WC2021] 表达式求值 题解

题目链接

点击打开链接

题目解法

不错的题

首先建出表达式树
说实话我一开始不知道怎么建,但看了代码之后就懂了
这里简单说一下:假如要对区间 \([l,r]\) 建树,分 \(E_r=)\)\(E_r\neq )\) 的情况

  • \(E_r=)\),找到匹配的左括号,递归下去建树
  • \(E_r\neq )\)\(r\) 可以作为单独的一个叶子,于是递归到 \([l,r-2]\)\([r,r]\) 建树

\(O(n|E|m)\) 是显然的
首先肯定是拆成数组的每一位做
里面做一遍树形 \(dp\) 即可,即 \(f_{i,j}\) 表示在结点 \(i\) 的结果为第 \(j\) 个数组的值,不难转移

考虑如何优化
这一步优化很巧妙,枚举最后的结果为第 \(x\) 个数组,记第 \(x\) 个数组当前位的值为 \(v\)(如果数值相同可以按照字典序排),那么最后的方案数只与比 \(v\) 大的集合有关,不妨枚举这个集合为 \(S\)
不难得到树形 \(dp\)\(f_{i,0/1/2}\) 表示在结点 \(i\) 的结果为 \(<v/=v/>v\) 的方案数,转移显然
最后值为 \(v\) 的方案数即为 \(f_{rt,1}\)
但这样的复杂度仍是 \(O(m2^m|E|)\),不能通过

考虑进一步优化,即如何不枚举 \(x\)
这里我们把最后的值类似地转成差分的形式,即我们改变最后需要求得的答案:最终结果 $\ge $ 在集合 \(S\) 中的方案数
这样只要令 \(f_{i,0/1}\) 表示结点 \(i\) 的结果为 \(<v/\ge v\) 的方案数
复杂度就降为了 \(O(2^m|E|)\)

最后统计答案显然
时间复杂度为 \(O(2^m|E|+nm\log m)\)

#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
inline int read(){
    int FF=0,RR=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    return FF*RR;
}
const int N=50010,M=10,P=1e9+7;
int n,m,a[M][N],id[M],g[1<<M];
char str[N];
int stk[N],top,brc[N];
int op[N],ind[N],lc[N],rc[N],idx;
int Get(int p){ if(str[p]=='>') return 1;if(str[p]=='<') return 0;return 2;}
int build(int l,int r){
    if(brc[r]){
        if(brc[r]==l) return build(l+1,r-1);
        int p=++idx;op[p]=Get(brc[r]-1);
        rc[p]=build(brc[r]+1,r-1),lc[p]=build(l,brc[r]-2);
        return p;
    }
    int p=++idx;
    if(l==r){ ind[p]=str[l]-48;return p;}
    op[p]=Get(r-1),rc[p]=build(r,r),lc[p]=build(l,r-2);
    return p;
}
int State,f[N][2];
inline void inc(int &x,int y){ x+=y;if(x>=P) x-=P;}
void dfs(int u){
    if(!lc[u]){ f[u][State>>ind[u]&1]=1;return;}
    dfs(lc[u]),dfs(rc[u]);
    if(op[u]==1||op[u]==2) F(i,0,1) F(j,0,1) inc(f[u][max(i,j)],1ll*f[lc[u]][i]*f[rc[u]][j]%P);
    if(!op[u]||op[u]==2) F(i,0,1) F(j,0,1) inc(f[u][min(i,j)],1ll*f[lc[u]][i]*f[rc[u]][j]%P);
}
int main(){
    n=read(),m=read();
    F(i,0,m-1) F(j,1,n) a[i][j]=read();
    scanf("%s",str+1);
    int len=strlen(str+1);
    F(i,1,len){
        if(str[i]=='(') stk[++top]=i;
        if(str[i]==')') brc[i]=stk[top],brc[stk[top]]=i,top--;
    }
    ms(ind,-1);
    int rt=build(1,len);
    F(S,0,(1<<m)-1){
        ms(f,0);State=S,dfs(rt);
        g[S]=f[rt][1];
    }
    int ans=0;
    F(i,1,n){
        F(j,0,m-1) id[j]=j;
        sort(id,id+m,[&](int x,int y){ return a[x][i]>a[y][i];});
        int St=0;inc(ans,1ll*g[(1<<m)-1]*a[id[m-1]][i]%P);
        F(j,0,m-2) St|=1<<id[j],inc(ans,1ll*g[St]*(a[id[j]][i]-a[id[j+1]][i])%P);
    }
    printf("%d\n",ans);
    return 0;
}

posted @ 2024-01-28 22:56  Farmer_D  阅读(26)  评论(0)    收藏  举报