matrix

题意

定义一个矩阵价值为它的不相同的行的个数
给出n*m大小的矩阵,求它的所有子矩阵的价值

题解

这个问题相当于对于每个(p,S)(p为左端点所在列,S为一个字符串(em…这里跳了一步,我们可以把数字序列看成字符串))在多少个(x,y)中满足 ∃ z ∈ [ x , y ] \exist z ∈[x,y] z[x,y],从z行p列开始的字符串和S相同
对于p=1,我们可以这样,将这N行看做是N个字符串,然后插入一个trie树中,这个trie树ch指针用map保存(因为字符集大小太大),并且用一个set存这个节点有多少个行有相同的信息,每次插入的时候,这一行的贡献就是他和他的前驱之间的距离乘上他和他的后继之间的距离
感性理解一下,这样就可以覆盖所有的对数了
并且存下这个节点的答案贡献
对于p=2,我们将trie树的第一层节点合并,那么像0 0 1 ;1 0 0这样的第二层就会变成相同的,于是我们就把这样相同的节点合并。我们使用启发式合并,每次节点数都可以减少一半,就可以在log内完成了,每次合并的时候也是暴力地把两个set小的合进大的,同时计算新的贡献,再减去原来的贡献,我们就得到了现在答案相比第一次答案的变化量,对于p=3也是同理的
p=1时 O ( n m l o g 2 n ) O(nmlog^2n) O(nmlog2n),之后每列都是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
综上总时间复杂度 O ( n m l o g 2 n ) O(nmlog^2n) O(nmlog2n)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef set<int>::iterator sit;
typedef map<int,int>::iterator mit;
const int N=5e5+5;
int n,m;
int tcnt=1,rt=1;
ll now;
struct node{
    ll val;
    map<int,int>ch;
    set<int>*s;
    node(){s=new set<int>();}
    void Insert(int x){
        int l=x+1,r=n-x;
        sit it=s->insert(x).first,it1;
        if(it!=s->begin()){
            it1=it;
            it1--;
            l=x-*it1;
        }
        it1=it;
        it1++;
        if(it1!=s->end()){
            r=*it1-x;
        }
        //printf("%d\n",l*r);
        val+=1ll*l*r;
        now+=1ll*l*r;
    }
 }trie[N];
int Merge(int x,int y){
    if(!x) return y;
    if(!y) return x;
    if(trie[x].ch.size()<trie[y].ch.size())
        swap(x,y);
    for(mit it=trie[y].ch.begin();it!=trie[y].ch.end();it++){
        trie[x].ch[it->first]=Merge(trie[x].ch[it->first],it->second);
    }
    if(trie[x].s->size()<trie[y].s->size()){
        swap(trie[x].s,trie[y].s);
        swap(trie[x].val,trie[y].val);
    }
    for(sit it=trie[y].s->begin();it!=trie[y].s->end();it++){
        trie[x].Insert(*it);
    }
    now-=trie[y].val;
    return x;
}
void Read(int &x){
    x=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
        x=x*10+c-'0',c=getchar();
}
int main()
{
    Read(n),Read(m);
    //scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        int p=rt;
        for(int j=0;j<m;j++){
            int x;
            Read(x);
            //scanf("%d",&x);
            if(trie[p].ch.find(x)==trie[p].ch.end())
                trie[p].ch[x]=++tcnt;
            p=trie[p].ch[x];
            trie[p].Insert(i);
        }
    }
    ll ans=0;
    ans+=now;
    for(int i=1;i<m;i++){
        int nrt=0;
        for(mit it=trie[rt].ch.begin();it!=trie[rt].ch.end();it++){
            nrt=Merge(nrt,it->second);
        }
        rt=nrt;
        now-=trie[rt].val;
        ans+=now;
    }
    printf("%lld\n",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值