给定按自上而下、自左而右编号的 n 层三角形麻将塔,初始被猫猫推倒若干块;若一块麻将的左右两个下方支撑都被推倒,则该块也会倒,求连锁反应最终倒下的总块数。
把每个编号先换成它在塔里的“第几行第几列”;然后从上往下一行一行看。每一行被猫抓倒的麻将会在这一行形成若干段连续的空位(区间),这些连续的空位会往上“吞”——段越长,它能连带倒掉上面越多的麻将。我们把能连带倒上面的连续段合并在一起,并记录每段还能继续支撑到哪一层,当某段不能再支撑时就把它移除。每处理一层,就根据当前所有段的长度和失效情况计算这一层新增倒下的块数并加到总数里,直到没有更多能倒的为止。这样就把连锁反应用“合并区间+按层推进”的方法高效算出来。
void solve(){
ll n,k;cin>>n>>k;
vector<pair<ll,ll>> a;
a.reserve(k);
for(int i=0;i<k;++i){
ll x;cin>>x;
ll l=1,r=n;
while(l<r){
ll m=(l+r)>>1;
__int128 t=(__int128)m*(m+1)/2;
if(t>=x){
r=m;
}else{
l=m+1;
}
}
ll row=l;
ll lev=n-row+1;
ll s=x-(ll)((__int128)(row-1)*row/2);
a.push_back({lev,s});
}
sort(all(a));
vll ls,ds;
vc ok;
map<ll,int> mp;
priority_queue<pair<ll,int>,vector<pair<ll,int>>,greater<pair<ll,int>>> pq;
ll h=a[0].fi;
ll sum=0,cnt=0;
__int128 ans=0;
int p=0;
auto clean=[&](){
while(!pq.empty()){
int id=pq.top().se;
if(ok[id]){
break;
}
pq.pop();
}
};
auto add=[&](ll x){
auto it=mp.lower_bound(x);
if(it!=mp.end()&&it->fi==x){
return;
}
ll l=x,r=x;
if(it!=mp.begin()){
auto jt=prev(it);
int id=jt->se;
ll e=ls[id]+ds[id]-h-1;
if(e>=x){
return;
}
if(e+1==x){
l=ls[id];
r=x;
sum-=ds[id]-h;
--cnt;
ok[id]=0;
mp.erase(jt);
}
}
it=mp.lower_bound(l);
while(it!=mp.end()){
int id=it->se;
ll L=ls[id];
if(L>r+1){
break;
}
r=max(r,ls[id]+ds[id]-h-1);
sum-=ds[id]-h;
--cnt;
ok[id]=0;
auto nx=next(it);
mp.erase(it);
it=nx;
}
ll len=r-l+1;
ll d=h+len;
int id=(int)ls.size();
ls.push_back(l);
ds.push_back(d);
ok.push_back(1);
mp[l]=id;
pq.push({d,id});
sum+=len;
++cnt;
};
while(p<k&&a[p].fi==h){
add(a[p].se);
++p;
}
ans+=sum;
while(h<n){
ll ns=p<k?a[p].fi:n+1;
clean();
ll nd=pq.empty()?n+1:pq.top().fi;
ll e=min(ns,nd);
if(e>n){
ll d=n-h;
ans+=(__int128)d*sum-(__int128)cnt*d*(d+1)/2;
break;
}
ll d=e-h;
if(d>0){
ll g=d-1;
ans+=(__int128)g*sum-(__int128)cnt*g*(g+1)/2;
sum-=d*cnt;
h=e;
}else{
h=e;
}
while(!pq.empty()){
int id=pq.top().se;
if(ok[id]&&ds[id]==h){
ok[id]=0;
pq.pop();
mp.erase(ls[id]);
--cnt;
}else{
if(!ok[id]){
pq.pop();
continue;
}
break;
}
}
while(p<k&&a[p].fi==h){
add(a[p].se);
++p;
}
ans+=sum;
}
auto out=[&](__int128 x){
if(x==0){
cout<<0<<endl;
return;
}
if(x<0){
cout<<"-";
x=-x;
}
string s;
while(x){
s.push_back(char('0'+x%10));
x/=10;
}
reverse(all(s));
cout<<s<<endl;
};
out(ans);
}

京公网安备 11010502036488号