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);
}