省選模擬賽Round3Day1 進攻!字符串 序列

 

當我看到題時,心態就已經崩了。。。

 

 

題解

這題其實就是求網格圖上的“希望”(有交K連通塊計數)

(這裏我們把一個面看成一個點,面面之間有公共邊則在兩個面之間連邊)

根據希望那道題的容斥方法(邊點容斥)

這種容斥的本質其實就是歐拉圖論定理V-E+F=2

歐拉圖論定理的適用範圍是平面圖,而網格圖恰好就是平面圖

我們先來計算每個點包含它的矩形的數目,再計算出每條邊包含它的矩形的數目,再算每個環包含它的矩形的數目

那麼最終的答案就等於Σ(點方案數^K)-Σ(邊方案數^K)+Σ(環方案數^K)

這樣我們就完成了第一步

 

我們現在來考慮如何計算包含一個點的方案

先算出這個點分別作爲左上、左下、右上、右下的矩形個數(利用單調隊列可以做到總體複雜度O(n^2))

一個矩形會使點它所覆蓋的點的方案數加1

於是我們考慮一種巧妙的差分

 

在計算出當前點的四種矩形個數後

一個1、4處的矩形會使當前點右下方所有點的方案數都加1

一個2、3處的矩形會使當前點右下方所有點的方案數都減1

爲什麼?

其實我們本應該對1、2、3、4處每一個合法的矩形進行差分

但是由於時間複雜度的限制,我們不能直接這樣做

而我們又考慮到:一個矩形,它本身就會被自己的四個頂點分別算到一次

所以我們可以分4次來對它進行差分

至於邊、環的方案也可以用類似的方法來做

這樣我們就解決了這道題

 

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 2005
#define LL long long
const int mod=998244353;
char s[N][N];
int h1[N][N],h2[N][N];
int stk[N],top;
LL f[4][N][N];
int ksm(int x,int y)
{
	int ret=1;
	while(y){
		if(y&1)ret=1ll*ret*x%mod;
		y>>=1;x=1ll*x*x%mod;
	}
	return ret;
}
int main()
{
	freopen("attack.in","r",stdin);
	freopen("attack.out","w",stdout);
	int n,m,K,i,j,k;
	scanf("%d%d%d",&n,&m,&K);
	for(i=1;i<=n;i++)scanf("%s",s[i]+1);
	for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(s[i][j]=='1')h1[i][j]=h1[i-1][j]+1;else h1[i][j]=0;
	for(i=n;i>=1;i--)for(j=1;j<=m;j++)if(s[i][j]=='1')h2[i][j]=h2[i+1][j]+1;else h2[i][j]=0;
	for(i=1;i<=n;i++){
		int sum=0;stk[top=1]=0;
		for(j=1;j<=m;j++){
			while(top&&h1[i][stk[top]]>=h1[i][j]){
				sum-=h1[i][stk[top]]*(stk[top]-stk[top-1]);
				top--;
			}
			stk[++top]=j;
			sum+=h1[i][j]*(stk[top]-stk[top-1]);
			f[0][i+1][j+1]+=sum;f[1][i+1][j+1]+=sum;f[2][i+1][j+1]+=sum;f[3][i+1][j+1]+=sum;
		}
	}
	for(i=1;i<=n;i++){
		int sum=0;stk[top=1]=m+1;
		for(j=m;j>=1;j--){
			while(top&&h1[i][stk[top]]>=h1[i][j]){
				sum-=h1[i][stk[top]]*(stk[top-1]-stk[top]);
				top--;
			}
			stk[++top]=j;
			sum+=h1[i][stk[top]]*(stk[top-1]-stk[top]);
			f[0][i+1][j]-=sum;f[1][i+1][j]-=sum;f[2][i+1][j+1]-=sum;f[3][i+1][j+1]-=sum;
		}
	}
	for(i=1;i<=n;i++){
		int sum=0;stk[top=1]=0;
		for(j=1;j<=m;j++){
			while(top&&h2[i][stk[top]]>=h2[i][j]){
				sum-=h2[i][stk[top]]*(stk[top]-stk[top-1]);
				top--;
			}
			stk[++top]=j;
			sum+=h2[i][stk[top]]*(stk[top]-stk[top-1]);
			f[0][i][j+1]-=sum;f[1][i+1][j+1]-=sum;f[2][i][j+1]-=sum;f[3][i+1][j+1]-=sum;
		}
	}
	for(i=1;i<=n;i++){
		int sum=0;stk[top=1]=m+1;
		for(j=m;j>=1;j--){
			while(top&&h2[i][stk[top]]>=h2[i][j]){
				sum-=h2[i][stk[top]]*(stk[top-1]-stk[top]);
				top--;
			}
			stk[++top]=j;
			sum+=h2[i][stk[top]]*(stk[top-1]-stk[top]);
			f[0][i][j]+=sum;f[1][i+1][j]+=sum;f[2][i][j+1]+=sum;f[3][i+1][j+1]+=sum;
		}
	}
	for(k=0;k<4;k++)for(i=1;i<=n;i++)for(j=1;j<=m;j++)
		f[k][i][j]=f[k][i][j]+f[k][i-1][j]+f[k][i][j-1]-f[k][i-1][j-1];
	int ans=0;
	for(i=1;i<=n;i++)for(j=1;j<=m;j++){
		for(k=0;k<4;k++)f[k][i][j]%=mod;
		ans=(1ll*ans+1ll*ksm(f[0][i][j],K)+1ll*mod-1ll*ksm(f[1][i][j],K)+1ll*mod-1ll*ksm(f[2][i][j],K)+1ll*ksm(f[3][i][j],K))%mod;
	}
	printf("%d\n",ans);
}

 

 

 

 

 

 

題解

Trie樹預處理+不均勻莫隊+刪除型回滾+雙向鏈表

這題

本來在考試的時候想到了莫隊,但是覺得莫隊會被卡爆(比如一個字符串特別長,還被不斷地刪除加入)

所以就寫了可持久化Trie樹暴力(竟然只有20分。。。)

沒想到可以在分塊的時候對字符串的長度加權,做一個不均勻的分塊

於是這道題就簡單很多了

在普通的Trie樹上可以隨便統計一個深度滿足條件的數的個數

然後利用set來插入刪除g[len],維護兩個連續點的差的平方和,再用總的方案數減去這個值就是合法區間的個數

然而O(nsqrt(n)logn)會TLE

我們考慮一種刪除很快的數據結構:雙向鏈表

這樣,利用類似與回滾莫隊的思路,我們先把所有的點都加入莫隊,右端點倒序進行莫隊

利用雙向鏈表來維護兩點差的平方和,這樣就只用刪除和回退了

雙向鏈表還有一個好處就是:刪除的時候不把當前點的指針清零,回退的時候直接按順序加入就可以了

 

代碼:(其實這份代碼有個bug:不能直接用權值之和來判斷是否存在該前綴,因爲權值可能爲0!!)

(但是數據太水了,所以過了hhhh)

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 600005
#define LL long long
int n,A,B,C;
int V[N];
char s[N];int le;
int ch[N][26];LL sum[N];
int tot,rt,mxl;
int all,pos[N],len[N],val[N],st[N],ed[N];
void insert(int &i,int d,int k)
{
	if(!i)i=++tot;
	pos[all+d+1]=i;len[i]=d;val[all+d+1]=k;
	if(d>=le)return;
	insert(ch[i][s[d]-'a'],d+1,k);
}
int bel[N],D;
struct node{
	int l,r,id;
	bool operator < (const node &t)const{
		return bel[l]<bel[t.l]||(bel[l]==bel[t.l]&&r>t.r);
	}
}q[N];
int g[N];
LL F(int x){return 1ll*x*(x-1)/2;}
LL gcd(LL x,LL y){return !y?x:gcd(y,x%y);}
int lp[N],rp[N],cnt[N];
bool check(int x){return (sum[x]&&1ll*B*sum[x]+1ll*A*len[x]>=1ll*C);}
LL ans,lans[N];
void add(int x)
{
	int p=pos[x],tmp=check(p);
	sum[p]+=val[x];
	if(!tmp&&check(p)&&(!cnt[g[len[p]]]++)){
		p=g[len[p]];
		lp[rp[p]]=p;
		rp[lp[p]]=p;
		ans+=F(rp[p]-p)+F(p-lp[p])-F(rp[p]-lp[p]);
	}
}
void del(int x)
{
	int p=pos[x],tmp=check(p);
	sum[p]-=val[x];
	if(tmp&&!check(p)&&(!(--cnt[g[len[p]]]))){
		p=g[len[p]];
		lp[rp[p]]=lp[p];
		rp[lp[p]]=rp[p];
		ans+=F(rp[p]-lp[p])-F(rp[p]-p)-F(p-lp[p]);
	}
}
int main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	int Q,l,r,L,R,i,j;
	n=gi();A=gi();B=gi();C=gi();
	for(i=1;i<=n;i++)V[i]=gi();
	for(i=1;i<=n;i++){
		scanf("%s",s);le=strlen(s);
		mxl=max(mxl,le);
		insert(rt,0,V[i]);
		st[i]=all+1;all+=le+1;ed[i]=all;
	}
	for(i=1;i<=mxl;i++)g[i]=gi();
	Q=gi();
	D=int(sqrt(1.0*all+0.5));
	for(i=1;i<=all;i++)bel[i]=(i-1)/D+1;
	for(i=1;i<=Q;i++){q[i].l=st[gi()];q[i].r=ed[gi()];q[i].id=i;}
	sort(q+1,q+Q+1);
	
	int las=0;ans=0;
	for(i=1;i<=all;i++)sum[pos[i]]+=val[i];
	for(i=1;i<=tot;i++)if(check(i))cnt[g[len[i]]]++;
	for(i=1;i<=mxl;i++)
		if(cnt[i]){rp[lp[i]=las]=i;ans+=F(i-las);las=i;}
	rp[las]=mxl+1;ans+=F(mxl+1-las);
	
	//printf("initial:");for(int k=1;k<=mxl;k++)printf("%d ",cnt[k]);printf("\n");
		
	l=1;r=all;
	for(i=1,j=1;i<=bel[all]&&j<=Q;i++){
		L=(i-1)*D+1;R=min(all,i*D);
		for(;j<=Q&&bel[q[j].l]==i;j++){
			
			//printf("start:");for(int k=1;k<=mxl;k++)printf("%d ",cnt[k]);printf("\n");
				
			while(r>q[j].r)del(r--);
			while(l<q[j].l)del(l++);
			
			
			//printf("now:");for(int k=1;k<=mxl;k++)printf("%d ",cnt[k]);printf("\n");
				
			lans[q[j].id]=ans;
			//printf("answer:%d\n",ans);
			while(l>L)add(--l);
				
			//printf("end:");for(int k=1;k<=mxl;k++)printf("%d ",cnt[k]);printf("\n");
			//printf("\n");
		}
		while(r<all)add(++r);
		while(l<R+1)del(l++);
	}
	for(i=1;i<=Q;i++){
		LL tmp=F(mxl+1);lans[i]=tmp-lans[i];
		LL gc=gcd(lans[i],tmp);
		printf("%lld/%lld\n",lans[i]/gc,tmp/gc);
	}
}

 

 

 

 

 

 

題解

整體二分+網絡流(這個問題本質上其實是保序迴歸L1問題)

考慮權值範圍爲[0,1]的情況

如圖就是一種建圖方法(藍邊邊權爲INF,綠邊邊權爲1)

可以發現這樣建圖的話,如果一個被限制爲最小值的點x的權值爲1,且它管轄的區間範圍種有權值爲0的點

那麼他就會貢獻1的流量,也就是說,使它滿足條件就會花費1的代價

這種建圖方式本質其實就是最大權閉合圖的建圖方式

注意,這裏要使用線段樹優化建邊,並且由於min、max限制的連邊方式普通,還需要開兩棵線段樹分別進行優化

然後根據保序迴歸的那篇論文,把所有的點整體二分一下最終的權值,然後看是否滿流就可以了???

表示看不懂論文

但是寫代碼還是會的。。。hhh

 

代碼:(啊啊啊cnt一定要賦成1啊啊啊啊,調到凌晨1點,難受,第二天才發現錯。。。)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 50005
#define M 1000005
const int INF=0x3f3f3f3f;
int n,m,val[N],ans;
struct qnode{
	int l,r,t;
	qnode(){}
	qnode(int x,int y,int z){l=x;r=y;t=z;}
};
vector<qnode> Q[N];
int fir[N],cur[N],nxt[M],to[M],cap[M],cnt;
void adde(int a,int b,int c)
{
	//printf("adde:%d %d %d\n",a,b,c);
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;cap[cnt]=c;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;cap[cnt]=0;
}
#define lc i<<1
#define rc i<<1|1
struct node{
	int l,r,x;
}a[2][N];
int id[N],tot;
void build(int i,int l,int r)
{
	a[0][i].l=l;a[0][i].r=r;
	if(l==r){a[0][i].x=a[1][i].x=id[l];return;}
	int mid=(l+r)>>1;
	a[0][i].x=++tot;a[1][i].x=++tot;
	build(lc,l,mid),build(rc,mid+1,r);
	adde(a[0][i].x,a[0][lc].x,INF);
	adde(a[0][i].x,a[0][rc].x,INF);
	adde(a[1][lc].x,a[1][i].x,INF);
	adde(a[1][rc].x,a[1][i].x,INF);
}
void insert(int i,int l,int r,int p,int t)
{
	if(a[0][i].l>r||a[0][i].r<l)return;
	if(l<=a[0][i].l&&a[0][i].r<=r){
		if(t)adde(a[1][i].x,p,INF);
		else adde(p,a[0][i].x,INF);
		return;
	}
	insert(lc,l,r,p,t);insert(rc,l,r,p,t);
}

int q[N],he,ta;
int S,T,dis[N];
bool bfs()
{
	memset(dis,-1,(tot+1)<<2);
	dis[q[he=ta=1]=T]=0;
	while(he<=ta){
		int u=q[he++];
		for(int v,p=fir[u];p;p=nxt[p]){
			if(cap[p^1]&&dis[v=to[p]]==-1){
				dis[v]=dis[u]+1;
				q[++ta]=v;
			}
		}
	}
	return dis[S]!=-1;
}
int sap(int u,int aug)
{
	if(u==T) return aug;
	int ret=0,tmp;
	for(int v,&p=cur[u];p;p=nxt[p])
		if(cap[p]&&dis[u]==dis[v=to[p]]+1){
			tmp=sap(v,min(cap[p],aug));
			cap[p]-=tmp;aug-=tmp;
			cap[p^1]+=tmp;ret+=tmp;
			if(aug==0)return ret;
		}
	return ret;
}
void solve(int l,int r,vector<int> &A)
{
	if(A.empty()) return;
	if(l==r){
		for(int i=0;i<(int)A.size();i++)
			ans+=(val[A[i]]<l?l-val[A[i]]:val[A[i]]-l);
		return;
	}
	if(A.size()==1){
		ans+=max(val[A[0]]-r,0)+max(l-val[A[0]],0);
		return;
	}
	int mid=(l+r)>>1,i,j;
	S=tot=1;T=++tot;
	for(i=0;i<(int)A.size();i++)
		id[i]=++tot;
	build(1,0,A.size()-1);
	for(i=0;i<(int)A.size();i++){
		for(j=0;j<(int)Q[A[i]].size();j++){
			qnode tmp=Q[A[i]][j];
			int x=lower_bound(A.begin(),A.end(),tmp.l)-A.begin();
			int y=upper_bound(A.begin(),A.end(),tmp.r)-A.begin()-1;
			if(x<=y) insert(1,x,y,id[i],tmp.t);
		}
	}
	for(i=0;i<(int)A.size();i++){
		if(val[A[i]]<=mid)adde(id[i],T,1);
		else adde(S,id[i],1);
	}
	while(bfs())
		memcpy(cur,fir,(tot+1)<<2),sap(S,INF);
	//bfs();
	vector<int> L,R;
	for(i=0;i<(int)A.size();i++){
		if(dis[id[i]]!=-1)L.push_back(A[i]);
		else R.push_back(A[i]);
	}
	memset(fir,0,(tot+1)<<2);cnt=1;
	solve(l,mid,L),solve(mid+1,r,R);
}
vector<int> ini;
int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	cnt=1;
	int i,x,mi=INF,mx=-INF,l,r,op;
	n=gi();m=gi();
	for(i=1;i<=n;i++){
		val[i]=gi();ini.push_back(i);
		mi=min(mi,val[i]);mx=max(mx,val[i]);
	}
	while(m--){
		op=gi();l=gi();r=gi();x=gi();
		Q[x].push_back(qnode(l,r,op));
	}
	solve(mi,mx,ini);
	printf("%d\n",ans);
}

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章