231025校内赛

T1 矩阵

一道容斥题我容斥一直都不是很好

对于所有的最大值限制来说可以按照 \(max\) 值来排序,每次处理所有相同的值

算出来之后可以直接删除当前行或列,因为剩余的都是更大的 \(max\) ,当前行或列自然满足后面的限制

假设有 \(r\)\(c\) 列的限制最大值等于 \(max\) ,那么这些位置不能超过 \(max\) 且每一行或列都有至少一个值取到 \(max\)

\(i,j\) 分别为有 \(i\)\(j\) 列没有取到最大值,\(t\) 表示 \(i\)\(j\) 列覆盖的位置数,\(tot\) 表示 \(r\)\(c\) 列总位置数,那么贡献就是 \((-1)^{i+j} \binom r i \binom c j (max-1)^t max^{tot-t}\)

注意:\(n,m\) 每次计算完之后都要减去 \(r,c\)

#include<bits/stdc++.h>
#define N 4010
#define ll long long
#define mod ((int)1e9+9)
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
int n,m,k,C[N][N];
pii p[N];
int ksm(int x,int y){
	int res = 1;
	while(y){
		if(y&1) res = 1ll*res*x%mod;
		x = 1ll*x*x%mod;
		y>>=1; 
	}
	return res;
}
int main(){
	freopen("mat.in","r",stdin);
	freopen("mat.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i = 1;i<=n;i++){
		k++;
		cin>>p[k].fi;
		p[k].se = 0;
	}
	cin>>m;
	for(int i = 1;i<=m;i++){
		k++;
		cin>>p[k].fi;
		p[k].se = 1;
	}
	sort(p+1,p+k+1);
	for(int i = 0;i<=k;i++){
		C[i][0] = 1;
		for(int j = 1;j<=i;j++)
			C[i][j] = (C[i-1][j-1]+C[i-1][j])%mod;
	}
	int ans = 1,res;
	for(int l = 1,r = 0;l<=k;l = r+1){
		res = 0;
		int cnt[2] = {0,0},mx = p[l].fi,lim[2];
		while(r+1<=k&&p[r+1].fi==p[l].fi)
			r++,cnt[p[r].se]++;
		lim[0] = cnt[0];lim[1] = cnt[1];
		if(!mx) lim[0] = lim[1] = 0;
		int v = ans,tot = (cnt[0]*m%mod+cnt[1]*n%mod-cnt[0]*cnt[1]%mod+mod)%mod;
		for(int pos = 0;pos<=lim[0];pos++){
			for(int c = 0;c<=lim[1];c++){
				int tmp = 1ll*C[cnt[0]][pos]*C[cnt[1]][c]%mod;
				int t = (pos*m%mod+c*n%mod-pos*c%mod+mod)%mod;
				tmp = 1ll*tmp*ksm(mx,t)%mod;
				tmp = 1ll*tmp*ksm(mx+1,tot-t)%mod;
				tmp = 1ll*tmp*v%mod;
				if((pos+c)&1) res = (res-tmp+mod)%mod;
				else res = (res+tmp)%mod;
			}
		}
		for(int i = l;i<=r;i++)
			if(p[i].se==0) n--;
			else m--;
		ans = res;
	}
	cout<<ans;
	return 0;
}

T2 图论

考虑修改操作对之后每次询问的影响,当询问 \(u\) 的点权时,往前找到最后一次能影响到 \(u\) 的赋值操作

再将这个赋值操作之后的所有能影响到 \(u\) 的取 \(min\) 操作拿出来,对修改权值一起求个最小值就是答案。

于是可以先用 \(bitset\) 预处理传递闭包,将所有的修改操作存在栈里,询问时在栈中找出所有有影响的操作,计算出点权

当栈的大小达到 \(O(\sqrt{n})\) 时,将栈中所有操作依次暴力执行并将栈清空即可,时间复杂度 \(O(\frac{n^2}{w} + n\sqrt{n})\)

如果最后两个测试点空间不够,可以考虑拿时间换空间

第一次选出 \(\frac{n}{k}\) 个点,只回答它们的询问,那么预处理传递闭包时也只需要记录每个点能否到达这 \(\frac{n}{k}\) 个点,这样做 \(k\) 次,每次将 \(bitset\) 的空间拿来重复利用

时间复杂度乘了 \(k\) ,空间复杂度除以了 \(k\)

#include<bits/stdc++.h>
#define N 50010
#define B 450
using namespace std;
struct edge{
	int v,ne;
}e[N];
int n,m,q,cnt,tim,hd,tl,h[N],opt[N],x[N],y[N],bl[N];
int f[130][N],tmp[N],val[N],ans[N],idx[N],vis[N];
bitset<(N>>1)>g[N];
bool cmp(int i,int j){
	return y[i]<y[j];
}
void add(int u,int v){
	e[++cnt].v = v;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
void cover(int u,int v,int *dp,int t){
	if(vis[u]==tim) return ;
	vis[u] = tim;dp[u] = v;idx[u] = t;
	for(int i = h[u];i;i = e[i].ne)
		cover(e[i].v,v,dp,t);
}
void getset(int u){
	if(vis[u]==tim) return ;
	vis[u] = tim;
	g[u].reset();
	if(u>=hd&&u<=tl) g[u][u-hd] = 1;
	for(int i = h[u];i;i = e[i].ne)
		getset(e[i].v),g[u]|=g[e[i].v];
}
int main(){
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m>>q;
	for(int i = 1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		add(u,v);
	}
	for(int i = 1;i<=q;i++){
		cin>>opt[i]>>x[i];
		if(opt[i]<=2) cin>>y[i];
		bl[i] = (i-1)/B+1;
	}
	for(int i = 1;i<=q;i++){
		if(bl[i]!=bl[i+1]){
			memset(f[bl[i]],0x3f,sizeof(f[bl[i]]));
			int len = 0;
			for(int j = i;bl[j]==bl[i];j--)
				if(opt[j]==2) tmp[++len] = j;
			sort(tmp+1,tmp+len+1,cmp);
			tim++;
			for(int j = 1;j<=len;j++)
				cover(x[tmp[j]],y[tmp[j]],f[bl[i]],0);
		}
	}
	hd = 1;tl = min(n,(N>>1));
	while(hd<=n){
		memset(val,0,sizeof(val));
		memset(idx,0,sizeof(idx));
		tim++;
		for(int i = 1;i<=n;i++)
			getset(i);
		for(int i = 1,len = 0;i<=q;i++){
			if(opt[i]==1){
				tmp[++len] = i;
				if(len==B){
					tim++;
					while(len){
						cover(x[tmp[len]],y[tmp[len]],val,tmp[len]);
						len--;
					}
				}
			}else if(opt[i]==3&&x[i]>=hd&&x[i]<=tl){
				int res = val[x[i]],l = idx[x[i]]+1,r = i-1;
				for(int j = len;j;j--)
					if(g[x[tmp[j]]][x[i]-hd]){
						res = y[tmp[j]];
						l = tmp[j]+1;
						break;
					}
				if(l>r);
				else if(bl[l]==bl[r]){
					for(int j = l;j<=r;j++)
						if(opt[j]==2&&g[x[j]][x[i]-hd])
							res = min(res,y[j]);
				}else{
					while(bl[l]==bl[l-1]){
						if(opt[l]==2&&g[x[l]][x[i]-hd]) res = min(res,y[l]);
						l++;
					}
					while(bl[r]==bl[r+1]){
						if(opt[r]==2&&g[x[r]][x[i]-hd]) res = min(res,y[r]);
						r--;
					}
					for(int j = bl[l];j<=bl[r];j++)
						res = min(res,f[j][x[i]]);
				}
				ans[i] = res;
			}
		}
		hd = tl+1;
		tl = min(n,hd+(N>>1)-1);
	}
	for(int i = 1;i<=q;i++)
		if(opt[i]==3) cout<<ans[i]<<"\n";
	return 0;
}

T3 排序

对于每一次的冒泡排序我们考虑它究竟会执行多少次

每个数前面最大数如果比它大那么就会被放到后面

所以一个数的答案就是它前面有多少个数大于它

设一个数的答案为 \(f_i\) ,所有的数的答案 \(\max{f_i}\)

考虑 \(f_i = i-g_i\) 那么 \(g_i\) 表示前面的 \(\le a_i\) 的数

但是有一个性质,如果有 \(i < j,a_i\ge a_j\) 那么 $f_i < f_j $

那么 \(g_i\) 就可以改为所有数中 \(\le a_i\) 的数,这样只会影响本就不会作为答案的 \(f_i\)

可以用权值线段树来实现

#include<bits/stdc++.h>
#define N 1000010
#define pii pair<int,int>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
struct node{
	int l,r,mx,flag;
}t[N<<2];
int n,m,q,a[N],x[N],y[N],lp[N],pos;
pii b[N],c[N];
void pushnow(int p,int v){
	t[p].mx+=v;t[p].flag+=v;
} 
void pushdown(int p){
	if(t[p].flag){
		pushnow(lc,t[p].flag);
		pushnow(rc,t[p].flag);
		t[p].flag = 0;
	}
}
void build(int p,int l,int r){
	t[p].l = l;t[p].r = r;
	if(l==r) return ;
	int mid = (l+r)>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
}
void add(int p,int l,int r,int v){
	if(t[p].l>=l&&t[p].r<=r){
		pushnow(p,v);
		return ;
	}
	pushdown(p);
	int mid = (t[p].l+t[p].r)>>1;
	if(l<=mid) add(lc,l,r,v);
	if(r>mid) add(rc,l,r,v);
	t[p].mx = max(t[lc].mx,t[rc].mx);
}
int main(){
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>q;
	for(int i = 1;i<=n;i++){
		cin>>a[i];
		c[i] = b[i] = {a[i],i};
	}
	for(int i = 1;i<=q;i++){
		cin>>x[i]>>y[i];
		b[i+n] = {y[i],i+n};
	}
	m = n+q;
	sort(b+1,b+m+1);
	for(int i = 1;i<=m;i++)
		if(b[i].fi==b[i-1].fi)
			lp[i] = lp[i-1];
		else lp[i] = i;
	build(1,1,m);
	for(int i = 1;i<=n;i++){
		pos = lower_bound(b+1,b+m+1,c[i])-b;
		add(1,pos,pos,i);
		add(1,lp[pos],m,-1);
	}
	for(int i = 1;i<=q;i++){
		pos = lower_bound(b+1,b+m+1,c[x[i]])-b;
		add(1,pos,pos,-x[i]);
		add(1,lp[pos],m,1);
		c[x[i]] = {y[i],i+q};
		pos = lower_bound(b+1,b+m+1,c[x[i]])-b;
		add(1,pos,pos,x[i]);
		add(1,lp[pos],m,-1);
		cout<<t[1].mx<<"\n";
	}
	return 0;
}
posted @ 2023-10-29 19:48  cztq  阅读(4)  评论(0编辑  收藏  举报