模擬賽20200312【區間gcd(DP),棋盤博弈模型,樹拓撲序逆序對總數】

T1:

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

題解:

因爲限制了區間gcd相等,直接DP的話需要知道當前區間以及它的gcd:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
Code(現在的人寫線段樹懶標記都不下傳的嗎。。。):

#include<bits/stdc++.h>
#define maxn 50005
#define fi first
#define se second
using namespace std;
const int mod = 998244353;
typedef pair<int,int> pii;
int n,m,tp,a[maxn];
pii S[maxn];
struct node{
	int w,x,l,r,t;
	bool operator < (const node &p)const{return w==p.w?x<p.x:w<p.w;}
}q[maxn*62];
int total,ans[maxn],f[maxn],g[maxn];
vector<int>P;
int sum[maxn<<2],tag[maxn<<2],ti[maxn<<2],tim;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline void clr(int i){if(ti[i]!=tim) ti[i]=tim,sum[i]=tag[i]=0;}
void ins(int i,int l,int r,int x,int y,int v){
	clr(i);
	sum[i]=(sum[i]+1ll*(min(r,y)-max(l,x)+1)*v)%mod;//no pushdown!!! tql!!!
	if(x<=l&&r<=y) {tag[i]=(tag[i]+v)%mod;return;}
	int mid=(l+r)>>1;
	if(x<=mid) ins(i<<1,l,mid,x,y,v);
	if(y>mid) ins(i<<1|1,mid+1,r,x,y,v);
}
int qry(int i,int l,int r,int x,int y){
	if(x>y||x>r||y<l) return 0;
	clr(i);
	if(x<=l&&r<=y) return sum[i];
	int mid=(l+r)>>1;
	return (1ll*(min(r,y)-max(l,x)+1)*tag[i]+qry(i<<1,l,mid,x,y)+qry(i<<1|1,mid+1,r,x,y))%mod;
}
void solve(int L,int R){
	tim++;
	for(int i=L;i<=R;i++){
		if(q[i].t==0) ins(1,1,n,q[i].x,n,(qry(1,1,n,q[i].l-1,q[i].r-1)+q[i].r-q[i].l+1)%mod);
		if(i==R||q[i].x!=q[i+1].x) f[q[i].x]=qry(1,1,n,q[i].x,q[i].x);
	}
	total=(total+f[q[R].x])%mod;
	ans[q[R].x+1]=(ans[q[R].x+1]+f[q[R].x])%mod;//k+1...n
	tim++;
	for(int i=R;i>=L;i--){
		if(q[i].t==1) ins(1,1,n,1,q[i].x,(qry(1,1,n,q[i].l+1,q[i].r+1)+q[i].r-q[i].l+1)%mod);
		if(i==L||q[i].x!=q[i-1].x) g[q[i].x]=qry(1,1,n,q[i].x,q[i].x);
	}
	P.clear(),P.push_back(q[L].x);
	for(int i=L+1;i<=R;i++) if(q[i].x!=q[i-1].x) P.push_back(q[i].x);
	for(int i=0,pre=0,now,dt;i<P.size();i++,pre=now){//pre+1...now-1, now.
		now=P[i];
		dt=(1ll*(f[pre]+1)*(g[now]+1)+mod-1)%mod;
		ans[pre+1]=(ans[pre+1]+dt)%mod,ans[now]=(ans[now]+mod-dt)%mod;
		dt=(1ll*(f[pre]+1)*(g[i<P.size()-1?P[i+1]:n+1]+1)+mod-1)%mod;
		ans[now]=(ans[now]+dt)%mod,ans[now+1]=(ans[now+1]+mod-dt)%mod;
	}
}
int main()
{
	//freopen("A.in","r",stdin);
	//freopen("A.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		S[++tp]=pii(a[i],i);
		for(int j=1;j<tp;j++) S[j].fi=gcd(S[j].fi,a[i]);
		int tmp=tp; tp=1;
		for(int j=2;j<=tmp;j++)
			if(S[j].fi==S[tp].fi) S[tp].se=S[j].se;
			else S[++tp]=S[j];
		for(int j=1;j<=tp;j++) q[++m]=(node){S[j].fi,i,S[j-1].se+1,S[j].se,0};
	}
	tp=0,S[0].se=n+1;
	for(int i=n;i>=1;i--){
		S[++tp]=pii(a[i],i);
		for(int j=1;j<tp;j++) S[j].fi=gcd(S[j].fi,a[i]);
		int tmp=tp; tp=1;
		for(int j=2;j<=tmp;j++)
			if(S[j].fi==S[tp].fi) S[tp].se=S[j].se;
			else S[++tp]=S[j];
		for(int j=1;j<=tp;j++) q[++m]=(node){S[j].fi,i,S[j].se,S[j-1].se-1,1};
	}
	sort(q+1,q+1+m);
	for(int i=1,j;i<=m;i=j+1){
		for(j=i;j<m&&q[i].w==q[j+1].w;j++);
		solve(i,j);
	}
	for(int i=1;i<=n;i++) ans[i]=(ans[i]+ans[i-1])%mod;
	for(int i=1;i<=n;i++) printf("%d%c",(total-ans[i]+mod)%mod,i==n?10:32);
}

T2:

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

題解:

在這裏插入圖片描述
在這裏插入圖片描述
實際上可以直接建[1,m][1,m],因爲i=1nlowbit(i)=n(n>>1)\bigoplus_{i=1}^nlowbit(i)=n\oplus(n>>1)

Code(注意此題按照題解的做法動態開點線段樹詢問時需要對一個完整區間詢問,因此訪問到當前點爲空時不能返回0):

#include<bits/stdc++.h>
#define maxn 100005
#define N 1073741823
#define lowbit(i) ((i)&-(i))
using namespace std;
int n,m,k,ans,rt,sz,lc[maxn*40],rc[maxn*40],cov[maxn*40],sx[maxn*40];
bool cnt[maxn*40];
struct node{int l,r,t;};
vector<node>q[maxn];
inline void pushup(int i,int l,int len){
	cnt[i]=cov[i]?len&1:cnt[lc[i]]^cnt[rc[i]];
	sx[i]=cov[i]?(lowbit(l)^(len>>1)):sx[lc[i]]^sx[rc[i]];
}
void ins(int &i,int l,int r,int x,int y,int v){
	if(!i) i=++sz;
	if(x<=l&&r<=y) {cov[i]+=v,pushup(i,l,r-l+1);return;}
	int mid=(l+r)>>1;
	if(x<=mid) ins(lc[i],l,mid,x,y,v);
	if(y>mid) ins(rc[i],mid+1,r,x,y,v);
	pushup(i,l,r-l+1);
}
bool Q_cnt(int i,int l,int r,int x,int y){
	if(!i||x>r||y<l) return 0;
	if(cov[i]) return (min(r,y)-max(l,x)+1)&1;
	if(x<=l&&r<=y) return cnt[i];
	int mid=(l+r)>>1;
	return Q_cnt(lc[i],l,mid,x,y)^Q_cnt(rc[i],mid+1,r,x,y);
}
int Q_sx(int i,int l,int r,int x,int y,bool flg){
	if(x>r||y<l) return 0;//!i don't return!!! Dynamic memory!
	flg|=cov[i];
	if(x<=l&&r<=y) return flg?(lowbit(l)^((r-l+1)>>1)):sx[i];
	int mid=(l+r)>>1;
	return Q_sx(lc[i],l,mid,x,y,flg)^Q_sx(rc[i],mid+1,r,x,y,flg);
}
int main()
{
	scanf("%d%d%d",&k,&n,&m);
	for(int i=1,a,b,c,d;i<=k;i++){
		scanf("%d%d%d%d",&a,&b,&c,&d);
		q[a].push_back((node){b,d,1}),q[c+1].push_back((node){b,d,-1});
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<q[i].size();j++) ins(rt,0,N,q[i][j].l,q[i][j].r,q[i][j].t);
		ans^=Q_cnt(rt,0,N,1,min(i,m))*lowbit(i);
		if(i<m) ans^=Q_sx(rt,0,N,i+1,m,0);
	}
	printf("%d\n",ans);
}

T3:

在這裏插入圖片描述
在這裏插入圖片描述
n500n\le500

題解:

因爲是求總數,可以考慮分開每一對逆序對乘上對應的方案數形成的貢獻,這樣就只需要記錄子樹中xx號節點排在拓撲序的第yy位時子樹拓撲序的方案數就可以DP了。
在這裏插入圖片描述
在這裏插入圖片描述
也有枚舉x<yx<y然後算yyxx前面的方案數的方法。不過8太會。
據說是O(n2h2)O(n^2h^2)的,傳說可以做到O(n2)O(n^2)

Code:

#include<bits/stdc++.h>
#define maxn 505
#define rep(i,j,k) for(int i=j,lim=k;i<=lim;i++)
using namespace std;
const int mod = 1e9+7;
int n,siz[maxn],c[maxn][maxn],f[maxn][maxn][maxn],g[maxn],w[maxn],s[maxn][maxn],h[maxn][maxn];
//f[i][j][k]:in subtree of i point k is at the jth place, indicate the valid ways.
//g:reverse pairs in all ways. w:all ways.
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
void merge(int x,int y){
	int wx=w[x],wy=w[y],sx=siz[x],sy=siz[y];
	g[x]=(1ll*g[x]*wy+1ll*g[y]*wx)%mod*c[sx+sy-1][sy]%mod;
	w[x]=1ll*wx*wy%mod*c[sx+sy-1][sy]%mod;
	rep(i,1,sx) rep(j,1,n) s[i][j]=h[i][j]=f[x][i][j],f[x][i][j]=0;
	rep(i,1,sx) rep(j,1,n) s[i][j]=(s[i][j]+s[i][j-1])%mod;
    rep(i,1,sx) rep(j,1,n) s[i][j]=(s[i][j]+s[i-1][j])%mod;
	rep(p,1,sy) rep(i,1,n) if(f[y][p][i]) rep(q,1,sx){
		int t=1ll*f[y][p][i]*c[p+q-2][p-1]%mod*c[sx-q+sy-p][sy-p]%mod;
		f[x][p+q][i]=(f[x][p+q][i]+1ll*t*wx)%mod;
		g[x]=(g[x]+t*(1ll*s[q][i-1]+s[sx][n]-s[sx][i]-s[q][n]+s[q][i]))%mod;
	}
	rep(p,1,sx) rep(i,1,n) if(h[p][i]) rep(q,0,sy){
		if(p==1&&q) break;
		int t=1ll*c[sx-p+sy-q][sy-q]*h[p][i]%mod;
		if(p>1) t=1ll*t*c[p-2+q][q]%mod;
		f[x][p+q][i]=(f[x][p+q][i]+1ll*t*wy)%mod;
	}
	siz[x]+=siz[y];
}
void dfs(int u,int ff){
	f[u][1][u]=1,g[u]=0,w[u]=1,siz[u]=1;
	for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff) dfs(v,u),merge(u,v);
}
int main()
{
	scanf("%d",&n);
	for(int i=(c[0][0]=1);i<=n;i++)
		for(int j=(c[i][0]=1);j<=i;j++)
			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
	for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),line(x,y),line(y,x);
	dfs(1,0);
	printf("%d\n",(g[1]+mod)%mod);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章