1. 程式人生 > 實用技巧 >7.28 NOI模擬賽 H2O 笛卡爾樹 並查集 貪心 長鏈剖分

7.28 NOI模擬賽 H2O 笛卡爾樹 並查集 貪心 長鏈剖分

LINK:H2O


這場比賽打的稀爛 爆蛋.

只會暴力.感覺暴力細節比較多不想寫.

其實這道題的難點就在於 採取什麼樣的策略放海綿貓.

知道了這一點才能確定每次放完海綿貓後的答案.

暴力列舉是不行的。而我們又想不到怎麼做?

此時需要考慮一維的情況 化簡問題 在數軸上進行貪心.

可以發現全域性最大值擋住了左右兩邊 也就是說左右兩邊是完全獨立的。

繼續思考 遞迴左邊此時區間全域性最大值也是如此.

一個容易觀察到的是 l和r相鄰 較大的那個一定在較小之後選擇.

那麼其實就是遞迴所有的地方來比較 從而進行選擇.

刪掉之後帶來的影響 其實是一路遞迴下來的樹的影響.

其實就是笛卡爾樹了 進一步發現每個節點和父親的邊刪掉都會有權值 這樣是我們進行第一次刪掉時的答案的體現.

從而發現第一次刪掉是最長鏈 那麼就可以長鏈剖分維護貪心了.

考慮二維的情況.

我們還是考慮把樹建立出來進行長鏈剖分.

怎麼建樹 全域性最大值肯定為根節點.

然後選出全域性次大的節點進行連邊 類比一下即可.

值得注意的是 正著做有聯通塊什麼的會不斷分裂。反著就可以用並查集合並.

排序之後反著做就可以把樹建出來了.

笛卡爾樹的建立也要學會 最近遇到的比較多了!

code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define gi(x) scanf("%lf",&x)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define fep(n,p,i) for(RE int i=n;i>=p;--i)
#define vep(p,n,i) for(RE int i=p;i<n;++i)
#define pii pair<int,int>
#define mk make_pair
#define RE register
#define P 1000000007ll
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define EPS 1e-10
#define sq sqrt
#define S second
#define F first
#define mod 1000000007
#define id(i,j) ((i-1)*m+j)
#define max(x,y) ((x)<(y)?y:x)
using namespace std;
char *fs,*ft,buf[1<<15];
inline char gc()
{
	return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
	RE int x=0,f=1;RE char ch=gc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
	return x*f;
}
const int MAXN=510,maxn=MAXN*MAXN;
int n,m,top,rt,k,len,num;
int a[maxn],f[MAXN],son[MAXN],sz[MAXN];
ll c[MAXN],mx[MAXN],ans,cnt;
int lin[maxn],ver[maxn],nex[maxn];
inline void add(int x,int y)
{
	ver[++len]=y;
	nex[len]=lin[x];
	lin[x]=len;
}
priority_queue<ll>q;
inline int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);}
struct wy
{
	int x,y;
	int z;
	inline bool operator <(wy a)const{return z<a.z;}
}t[maxn];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
inline void dfs(int x)
{
	sz[x]=1;
	go(x)
	{
		dfs(tn);
		c[tn]=(ll)(a[x]-a[tn])*sz[tn];
		ans+=c[tn];mx[tn]+=c[tn];
		if(mx[tn]>mx[x])
		{
			mx[x]=mx[tn];
			son[x]=tn;
		}
		sz[x]+=sz[tn];
	}
}
inline void dp(int x,int fa)
{
	if(x==fa)q.push(mx[x]);
	if(son[x])dp(son[x],fa);
	go(x)if(tn!=son[x])dp(tn,tn);
}
int main()
{
	//freopen("1.in","r",stdin);
	get(n);get(m);get(k);
	rep(1,n,i)rep(1,m,j)
	{
		int get(x);
		f[id(i,j)]=id(i,j);
		t[++num]=(wy){i,j,x};
	}
	//cout<<id(1,2)<<endl;
	sort(t+1,t+1+num);
	//cout<<t[1].z<<endl;
	rep(1,num,i)
	{
		int x=t[i].x;
		int y=t[i].y;
		int id=id(x,y);
		rep(0,3,k)
		{
			int xx=dx[k]+x;
			int yy=dy[k]+y;
			if(xx<1||yy<1||xx>n||yy>m)continue;
			if(!a[id(xx,yy)])continue;
			int w1=getfather(id(xx,yy));
			if(w1==id)continue;
			f[w1]=id;add(id,w1);
		}
		a[id]=t[i].z;
	}
	rt=id(t[num].x,t[num].y);
	//put(rt);
	//cout<<getfather(1)<<endl;
	dfs(rt);dp(rt,rt);
	while(q.size()&&k)
	{
		ans-=q.top();
		cnt=cnt^ans;
		q.pop();--k;
	}
	putl(cnt);
	return 0;
}