• 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 [igeq a_i]),其中(a)是值的数组。
    (a)从小到大排序。显然(a_{i-1}+1 o a_i)([igeq a_i])是相同的,可以把([igeq 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(nmlog_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);
    }
    
  • 相关阅读:
    UML类图基础说明
    grep: /usr/include/php/main/php.h: No such file or directory
    活在幻梦中的你我
    Markdown语法笔记
    眼见为实
    潜意识与暗示
    蝌蚪与青蛙是同一个物种么?
    PHP 数组函数整理
    git 笔记
    vue2.0-组件传值
  • 原文地址:https://www.cnblogs.com/ctmlpfs/p/14434990.html
Copyright © 2020-2023  润新知