expr

这道题的trick我见过好几次了。
但是在考场上一点印象也没有。
还好在学普及组时学了表达式树,成功获得70分。
先建立原串的表达式树。
容易发现数组的每个元素是独立的。
于是问题转化为:\(O(n)\)次给定一个数组\(ar\),给定一个固定的树,每个叶子节点上有固定的权值\(b\)
询问时把叶子节点\(i\)权值改为\(ar_{b_i}\)
树上每个非叶子节点是\(\min\)或者\(\max\)节点,表示这个节点的值是儿子节点的\(\min\)或者\(\max\)。询问根节点的值。
根节点的值只有\(O(m)\)种,考虑每个值对答案的贡献,设值\(v\)出现了\(y_v\)次,就是\(\sum_i y_v*v\)
使用等于转大于转化,就是要求\(\sum_i [i\geq a_i]\),其中\(a\)是值的数组。
\(a\)从小到大排序。显然\(a_{i-1}+1\to a_i\)\([i\geq a_i]\)是相同的,可以把\([i\geq a_i]\)算出后累加\(a_i-a_{i-1}\)次。
接下来就只用关心每个值和\(i\)的大小关系。
根据heoi某题,把\(\geq i\)的赋值成\(1\),把\(<i\)的赋值为0。求出根节点为\(0/1\)的方案数,可以用dp。
\(f_{i,0/1}\)表示以\(i\)为根的子树的值为\(0/1\)的方案数,转移显然。
但是这样子对时间复杂度没有改进。
由于\(m\)非常小,所以可以预处理出\(2^m\)种叶子结点取值为\(0/1\)的情况,然后可以\(O(1)\)查询。
最终时间复杂度\(O(nm\log_2m+|E|2^m)\)

#include<bits/stdc++.h>
using namespace std;
#define mo 1000000007
#define N 200010
#define int long long
struct no{
	int x,y;
}s2[N],b[N];
int operator <(no x,no y){
	return x.x<y.x;
}
int operator ==(no x,no y){
	return x.x==y.x;
}
int n,m,a[N][12],ct,s1[N],t1,t2,op[N],lc[N],rc[N],s3[N],s4[N],ans[N][2],f[N][2],rt,va[N];
char s[N];
void dfs(int x){
	int l=lc[x],r=rc[x];
	if(l)
		dfs(l);
	if(r)
		dfs(r);
	if(!l&&!r)
		f[x][va[x]]=1;
	else{
		if(op[x]!=-1){
			f[x][1]=(f[l][1]*f[r][1]%mo+f[l][0]*f[r][1]%mo+f[l][1]*f[r][0]%mo)%mo;
 			f[x][0]=f[l][0]*f[r][0]%mo;
		}
		if(op[x]!=-2){
			f[x][1]=(f[x][1]+f[l][1]*f[r][1])%mo;
			f[x][0]=(f[x][0]+f[l][0]*f[r][0]%mo+f[l][1]*f[r][0]%mo+f[l][0]*f[r][1]%mo)%mo;
		}
	}
}
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=0;i<m;i++)
		for(int j=1;j<=n;j++)
			scanf("%lld",&a[j][i]);
	scanf("%s",s+1);
	int l=strlen(s+1);
	s[0]='(';
	s[l+1]=')';
	for(int i=0;i<=l+1;i++){
		if(isdigit(s[i])){
			s1[++t1]=++ct;
			op[ct]=s[i]-'0';
		}
		else if(s[i]=='>'||s[i]=='<'||s[i]=='?'){
			s2[++t2]=(no){++ct,1};
			if(s[i]=='<')
				op[ct]=-1;
			else if(s[i]=='>')
				op[ct]=-2;
			else
				op[ct]=-3;
		}
		else if(s[i]=='(')
			s2[++t2]=(no){0,0};
		else{
			int t3=0,t4=0,cc=0;
			while(s2[t2].y){
				s3[++t3]=s2[t2].x;
				t2--;
				cc++;
			}
			t2--;
			for(int j=1;j<=cc+1;j++){
				s4[++t4]=s1[t1];
				t1--;
			}
			reverse(s4+1,s4+t4+1);
			reverse(s3+1,s3+t3+1);
			int la=s4[1],ll;
			for(int i=1;i<=t3;i++){
				lc[s3[i]]=la;
				rc[s3[i]]=s4[i+1];
				la=s3[i];
				rt=s3[i];
			}
			s1[++t1]=la;
		}
	}
	for(int i=0;i<(1<<m);i++){
		for(int j=1;j<=ct;j++)
			va[j]=f[j][0]=f[j][1]=0;
		for(int j=1;j<=ct;j++)
			if(op[j]>=0){
				if(i&(1<<op[j]))
					va[j]=1;
				else
					va[j]=0;
			}
		dfs(rt);
		ans[i][0]=f[rt][0];
		ans[i][1]=f[rt][1];
	}
	int vv=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<m;j++)
			b[j]=(no){a[i][j],j};
		sort(b,b+m);
		reverse(b,b+m);
		int va=0;
		for(int j=1;j<=m;j++){
			for(int k=0;k<m;k++)
				if((!(va&(1<<b[k].y)))&&b[j-1].x==b[k].x)
					va+=(1<<b[k].y);
			vv=(ans[va][1]*(b[j-1].x-b[j].x)%mo+vv)%mo;
		}
	}
	printf("%lld",vv);
}
posted @ 2021-02-23 11:19  celerity1  阅读(207)  评论(0编辑  收藏  举报