20200612 hz【矩形容斥,鏈表+回滾莫隊,保序迴歸L1】

T1:「雅禮集訓 2018 Day11」進攻!

題目描述:

在這裏插入圖片描述
n,m2000,k106n,m\le2000,k\le10^6
LOJ link

題目分析:

求選K個全1矩形使其有交的方案數。
考慮容斥,計算每個1×11\times 1的矩形被多少個矩形包含,設爲ss,那麼答案加上sks^k
然後看多算了什麼,假設某個方案中矩形的交的大小是一個xyx*y的矩形,那麼它被算了xyx*y次。
減去1×21\times 2的矩形,2×12\times 1的矩形,加上2×22\times 2的矩形。
那麼一個交爲xyx*y的方案被算了 xyx(y1)y(x1)+(x1)(y1)=1x*y-x(y-1)-y(x-1)+(x-1)(y-1)=1 次。
原理可以理解爲 點 - 邊 + 環 = 1,矩形是一個天然的簡單環結構。

那麼問題就是計算 1/2×1/21/2\times 1/2 的矩形被多少個全1矩形包含。

先考慮 1×11\times 1
一個全1矩形會對內部的點有1的貢獻,差分之後就是四個角+1或-1,但是矩形並不是輸入的,不太好分開一個一個加。
求出以每個點爲右下角的全1矩形個數,就可以算出右下角的+1的差分值;同理,求出以每個點爲左上/左下/右下的全1矩形個數,就可以算出每個位置的差分值。最後求前綴和就可以得到每個點被覆蓋的次數。

對於其它情況類似。求以每個點爲右下角的全1矩形個數可以單調隊列(延伸作限制,維護單調上升的立柱)
在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述
Code:

#include<bits/stdc++.h>
#define maxn 2005
#define rep(i,j,k) for(int i=(j),lim=(k);i<=lim;i++)
using namespace std;
const int mod = 998244353;
int n,m,K,s[maxn][maxn],f[maxn][maxn],d[maxn][maxn][4],ans;
char a[maxn][maxn];
int q[maxn],tp;
int Pow(int a,int b){int s=1;for(;b;b>>=1,a=1ll*a*a%mod) b&1&&(s=1ll*s*a%mod);return s;}
void solve(){
	rep(i,1,n){
		int res=0; tp=0;
		rep(j,1,m){
			s[i][j]=a[i][j]=='1'?s[i-1][j]+1:0;
			for(;tp&&s[i][q[tp]]>=s[i][j];tp--) res-=(q[tp]-q[tp-1])*s[i][q[tp]];
			res+=(j-q[tp])*s[i][j], q[++tp]=j;
			f[i][j]=res;
		}
	}
}
int main()
{
	//freopen("attack.in","r",stdin);
	//freopen("attack.out","w",stdout);
	scanf("%d%d%d",&n,&m,&K);
	for(int i=1;i<=n;i++) scanf("%s",a[i]+1);
	
	solve();
	rep(i,1,n) rep(j,1,m) rep(k,0,3) d[i+1][j+1][k]=f[i][j];
	
	rep(i,1,n) reverse(a[i]+1,a[i]+1+m);
	solve();
	rep(i,1,n) rep(j,1,m) rep(k,0,3) d[i+1][j+(k&1)][k]-=f[i][m-j+1];
	
	reverse(a+1,a+1+n);
	solve();
	rep(i,1,n) rep(j,1,m) rep(k,0,3) d[i+(k>=2)][j+(k&1)][k]+=f[n-i+1][m-j+1];
	
	rep(i,1,n) reverse(a[i]+1,a[i]+1+m);
	solve();
	rep(i,1,n) rep(j,1,m) rep(k,0,3) d[i+(k>=2)][j+1][k]-=f[n-i+1][j];
	
	rep(i,1,n) rep(j,1,m) rep(k,0,3)
		d[i][j][k]=(1ll*d[i][j][k]+d[i-1][j][k]+d[i][j-1][k]-d[i-1][j-1][k])%mod,
		ans=(ans+(!k||k==3?1:-1)*Pow(d[i][j][k],K))%mod;
	printf("%d\n",(ans+mod)%mod);
}

T2:「雅禮集訓 2018 Day11」字符串

在這裏插入圖片描述
LOJ6517

N,M105,si3105N,M\le10^5,\sum|s_i|\le3*10^5

題目分析:

莫隊。初步嘗試用 Trie樹 + set 維護答案
直接對 nn 莫隊的話,可能每次都加入一個很長的串。

發現串總長3105\le 3*10^5,即每個串的前綴的個數3105\le 3*10^5
那麼把串接起來,記錄每個串的start,endstart,end,原先對[l,r][l,r]的詢問就相當於將[stl,edr][st_l,ed_r]的點對應前綴加入後的答案。於是可以對此莫隊。複雜度O((si)mlogmaxlen)O\left((\sum|s_i|)*\sqrt m\log maxlen\right)

考慮把setset換成鏈表,但是鏈表只支持刪除,不支持插入(沒法找插入前驅後繼),但是它支持刪除後按順序回退,所以用回滾莫隊,初始時全部加入。每次把左指針指到塊左端點,右指針由大到小刪除,最後回退即可。

注意維護答案的時候沒有出現的前綴可能Alen(S)CA*len(S)\ge C,但是不能算進去,要判斷f(S)f(S)是否爲空(數據沒有vi=0v_i=0的情況)

Code:

#include<bits/stdc++.h>
#define maxn 300005
#define LL long long
using namespace std;
void read(int &a){
	char c;while(!isdigit(c=getchar()));
	for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
const int S = 900;
int n,m,A,B,C,W[maxn],sum,mxl,g[maxn],pos[maxn],st[maxn],ed[maxn],bel[maxn];
LL ans[maxn],res,f[maxn];
struct node{
	int l,r,id;
	bool operator < (const node &p)const{return bel[l]==bel[p.l]?r>p.r:bel[l]<bel[p.l];}
}q[maxn];
char s[maxn];
int ch[maxn][26],sz,w[maxn],len[maxn],cnt[maxn],L[maxn],R[maxn];
void insert(char *s,int len,int val){
	int r=0,v;
	for(int i=0;i<len;i++){
		if(!ch[r][v=s[i]-'a']) ch[r][v]=++sz,::len[sz]=i+1;
		pos[++sum]=r=ch[r][v],w[sum]=val;
	}
}
LL F(int x){return 1ll*x*(x-1);}
bool chk(int x){return !f[x]?0:1ll*B*f[x]+1ll*A*len[x]>=C;}
void ins(int x){
	int p=pos[x],pre=chk(p); f[p]+=w[x];
	if(!pre&&chk(p)&&!cnt[g[len[p]]]++)
		p=g[len[p]],res+=F(p-L[p])+F(R[p]-p)-F(R[p]-L[p]),R[L[p]]=L[R[p]]=p;
}
void del(int x){
	int p=pos[x],pre=chk(p); f[p]-=w[x];
	if(pre&&!chk(p)&&!--cnt[g[len[p]]])
		p=g[len[p]],res+=F(R[p]-L[p])-F(p-L[p])-F(R[p]-p),R[L[p]]=R[p],L[R[p]]=L[p];
}
int main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	read(n),read(A),read(B),read(C);
	for(int i=1;i<=n;i++) read(W[i]);
	for(int i=1;i<=n;i++){
		scanf("%s",s); int len=strlen(s); mxl=max(mxl,len);
		st[i]=sum+1,ed[i]=sum+len,insert(s,len,W[i]);
	}
	for(int i=1;i<=mxl;i++) read(g[i]);
	for(int i=1;i<=sum;i++) bel[i]=(i-1)/S;
	read(m);
	for(int i=1,x,y;i<=m;i++) read(x),read(y),q[i].l=st[x],q[i].r=ed[y],q[i].id=i;
	for(int i=1;i<=sum;i++) f[pos[i]]+=w[i];
	for(int i=1;i<=sz;i++) if(chk(i)) cnt[g[len[i]]]++;
	int last=0;
	for(int i=1;i<=mxl;i++) if(cnt[i]) R[L[i]=last]=i,res+=F(i-last),last=i;
	R[last]=mxl+1,res+=F(R[last]-last);
	sort(q+1,q+1+m);
	for(int k=0,i=1;k*S+1<=sum&&i<=m;k++){
		int lb=k*S+1,rb=min((k+1)*S+1,sum+1),x=lb,y=sum;
		for(;i<=m&&bel[q[i].l]==k;i++){
			while(y>q[i].r) del(y--);
			while(x<q[i].l) del(x++);
			ans[q[i].id]=res;
			while(x>lb) ins(--x);
		}
		while(y<sum) ins(++y);
		while(x<rb) del(x++);
	}
	for(int i=1;i<=m;i++){
		LL y=F(mxl+1),x=y-ans[i],d=__gcd(x,y);
		printf("%lld/%lld\n",x/d,y/d);
	}
}

T3:「雅禮集訓 2018 Day11」序列

在這裏插入圖片描述
在這裏插入圖片描述
LOJ6518

N5000,M15000,ai105N\le5000,M\le15000,a_i\le10^5

題目分析:

偏序關係,最小化xiai\sum |x_i-a_i|,弱化的保序迴歸L1L_1問題,整體二分,每次求出向S{mid,mid+1}S\{mid,mid+1\}取整的最優解,然後劃分。詳見 IOI2018集訓隊論文《淺談保序迴歸問題》

Code:

#include<bits/stdc++.h>
#define maxn 50005
#define maxm 500005
#define pb(x) push_back(x)
#define rep(i,j,k) for(int i=(j),lim=(k);i<=lim;i++)
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m,a[maxn],ans;
struct node{int l,r,t;};
vector<node>Q[maxn];
int S,T,dis[maxn];
int fir[maxn],cur[maxn],nxt[maxm],to[maxm],c[maxm],tot=1;
void line(int x,int y,int z=inf){
	nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,c[tot]=z;
	nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,c[tot]=0;
}
#define lc i<<1
#define rc i<<1|1
int id[maxn],tr[maxn][2],sz;
void build(int i,int l,int r){
	if(l==r) return void(tr[i][0]=tr[i][1]=id[l]);
	int mid=(l+r)>>1;
	tr[i][0]=++sz,tr[i][1]=++sz;
	build(lc,l,mid),build(rc,mid+1,r);
	line(tr[i][0],tr[lc][0]),line(tr[i][0],tr[rc][0]);
	line(tr[lc][1],tr[i][1]),line(tr[rc][1],tr[i][1]);
}
void ins(int i,int l,int r,int x,int y,int p,int t){
	if(x<=l&&r<=y) {!t?line(p,tr[i][0]):line(tr[i][1],p); return;}
	int mid=(l+r)>>1;
	if(x<=mid) ins(lc,l,mid,x,y,p,t);
	if(y>mid) ins(rc,mid+1,r,x,y,p,t);
}
bool BFS(){
	static int q[maxn],l,r;
	memset(dis,-1,(sz+1)<<2),dis[q[l=r=1]=T]=0;
	while(l<=r){
		int u=q[l++];
		for(int i=fir[u],v;i;i=nxt[i]) if(c[i^1]&&dis[v=to[i]]==-1)
			dis[q[++r]=v]=dis[u]+1;
	}
	return ~dis[S];
}
int aug(int u,int augco){
	if(u==T) return augco;
	int need=augco,dt;
	for(int &i=cur[u];i;i=nxt[i]) if(c[i]&&dis[u]==dis[to[i]]+1){
		dt=aug(to[i],min(c[i],need)),c[i]-=dt,c[i^1]+=dt;
		if(!(need-=dt)) return augco;
	}
	return augco-need;
}
void solve(int l,int r,vector<int>&A){
	if(l==r) {for(int x:A) ans+=abs(a[x]-l); return;}
	if(A.empty()) return;
	if(A.size()==1) {int x=A[0]; ans+=max(a[x]-R,0)+max(L-a[x],0); return;}
	int mid=(l+r)>>1;
	S=sz=1,T=++sz;
	rep(i,0,A.size()-1) id[i]=++sz;
	build(1,0,A.size()-1);
	rep(i,0,A.size()-1)
		for(node q:G[A[i]]){
			int x=lower_bound(A.begin(),A.end(),q.l)-A.begin(),y=upper_bound(A.begin(),A.end(),q.r)-A.begin()-1;
			if(x<=y) ins(1,0,A.size()-1,x,y,id[i],q.t);
		}
	rep(i,0,A.size()-1) a[A[i]]<=mid ? line(id[i],T,1) : line(S,id[i],1);
	while(BFS()) memcpy(cur,fir,(sz+1)<<2),aug(S,inf);
	BFS();
	vector<int>L,R;
	rep(i,0,A.size()-1) dis[id[i]]!=-1 ? L.pb(A[i]) : R.pb(A[i]);
	memset(fir,0,(sz+1)<<2),tot=1;
	solve(l,mid,L),solve(mid+1,r,R);
}
int main()
{
	//freopen("sequence.in","r",stdin);
	//freopen("sequence.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int x;m--;) {node q; scanf("%d%d%d%d",&q.t,&q.l,&q.r,&x),Q[x].pb(q);}
	vector<int>A;
	for(int i=1;i<=n;i++) A.pb(i);
	solve(*min_element(a+1,a+1+n),*max_element(a+1,a+1+n),A);
	printf("%d\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章