Winter Camp 2019 Simulation Day5 T1 Matrix(Trie樹合併)
阿新 • • 發佈:2019-01-13
有位大佬把
和
打了個拼盤然後拿了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);
}