版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/86253356
有位大佬把
和
打了个拼盘然后拿了90分%%%
正解以前完全没意识过。
把每一行看做一个字符串加入trie树,然后就可以用做字符串题的方法来做这道题,具体就是先把矩阵的左边界看做是1,求出每个字符串在哪几行出现,那么就可以统计出有多少个行区间包含这个字符串,又每个字符串确定了矩形的长,那么trie树中每个点行区间的个数就是方案数。
把左边界右移的时候,(居然)可以把根的所有儿子合并,trie树合并和线段树合并差不多,然后用set的启发式合并继续维护左端点右移后的每个字符串在哪几行出现,之后就这样。
复杂度
PS:可以用Splay代替set做到一个
但是我个人。。。。。。不想打。
AC Code:
#include<bits/stdc++.h>
#define maxn 500005
#define LL long long
using namespace std;
int n,m;
struct mat{ int f[maxn*2];int* operator[](int x){ return f+x*m; } }A;
int rt,tot;
LL val[maxn],sum,ans;
set<int>st[maxn];
map<int,int>mp[maxn];
set<int>::iterator prev(set<int>::iterator it)
{
it--;
return it;
}
void Insert(int id,int x)
{
int Llen = x , Rlen = n-x+1;
set<int>::iterator it = st[id].lower_bound(x);
if(it!=st[id].begin())
Llen = x - *prev(it);
if(it!=st[id].end())
Rlen = *it - x;
st[id].insert(x);
val[id] += 1ll * Llen * Rlen;
sum += 1ll* Llen * Rlen;
}
void Merge(int &now,int lc,int rc)
{
if(!lc || !rc) {now = lc+rc; return;}
if(mp[lc].size() < mp[rc].size()) swap(lc,rc);
for(map<int,int>::iterator it = mp[rc].begin();it!=mp[rc].end();it++)
Merge(mp[lc][(*it).first],mp[lc][(*it).first],(*it).second);
if(st[lc].size() < st[rc].size())
{
swap(st[lc],st[rc]);
swap(val[lc],val[rc]);
}
for(set<int>::iterator it=st[rc].begin();it!=st[rc].end();it++)
Insert(lc,*it);
sum -= val[rc];
now = lc;
}
int main()
{
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
scanf("%d%d",&n,&m);
int tot = 0;
for(int i=1;i<=n;i++)
for(int j=1,p=rt;j<=m;j++)
{
scanf("%d",&A[i][j]);
if(!mp[p].count(A[i][j])) mp[p][A[i][j]] = ++tot;
p = mp[p][A[i][j]];
Insert(p,i);
}
ans += sum;
for(int i=1;i<m;i++)
{
int nrt = -1;
for(map<int,int>::iterator it = mp[rt].begin() ; it!=mp[rt].end() ; it++)
{
if(nrt == -1)
nrt = (*it).second;
else
Merge(nrt,nrt,(*it).second);
}
sum -= val[nrt];
ans += sum;
rt = nrt;
}
printf("%lld\n",ans);
}