『模拟赛』暑假集训CSP提高模拟17

『模拟赛』暑假集训CSP提高模拟17

日常挂分:T4 \(\color{purple}{RE}\) -40pts

不愧是我先天\(\color{purple}{RE}\)圣体

题目真的和题面有关系吗?哦,只和题目背景有关。

T1 符号化方法初探

赛时乱搞前缀和,思路没有那么清晰。骗的分还挺多...

正解如果全正(负)就直接 \(n-1\) 次前(后)缀合碾过去,否则记一下绝对值最大的数,经过 \(n-1\) 次操作把她们转成同正(负),之后前(后)缀合跑一遍,最多操作 \(2n-2\) 次。

int n,cnt,a[N];
int mx=0,mn=inf,pos;
struct Move{
	int i,j;
}q[N*4];

signed main(){
	n=rd;
	for(int i=1;i<=n;i++){
		a[i]=rd;
		if(abs(a[i])>abs(mx)){
			mx=a[i];
			pos=i;
		}
	}
	
	for(int i=1;i<=n;i++){
		q[++cnt].i=pos,q[cnt].j=i;
	}
	if(mx>0){
		for(int i=2;i<=n;i++){
			q[++cnt].i=i-1,q[cnt].j=i;
		}
	}else if(mx<0){
		for(int i=n-1;i;i--){
			q[++cnt].i=i+1,q[cnt].j=i;
		}
	}else{
		printf("0");
		return Elaina;
	}
	printf("%lld\n",cnt);
	for(int i=1;i<=cnt;i++){
		printf("%lld %lld\n",q[i].i,q[i].j);
	}
	return Elaina;
}

T2 无标号 Sequence 构造

赛时乱糊了一个矩阵乘。

但直接 \(n^3\) 乘的复杂度显然不可接受,考虑随机选点判定。

int n,m;
int a[N][N],b[N][N],c[N][N];
bool vis[N][N];

void cleanall(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i][j]=b[i][j]=c[i][j]=vis[i][j]=0;
		}
	}
}

signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	
	srand(time(0));
	int T;
	cin>>T;
	while(T--){
		cleanall();
		cin>>n;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				cin>>a[i][j];
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				cin>>b[i][j];
			}
		}
		int flg=1;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				cin>>c[i][j];
			}
		}
		int mn=min(1ll*n*n,60000ll);
		for(int k=1;k<=mn;k++){
            int x=random(1,n),y=random(1,n),cnt=0;
            if(vis[x][y]){
                k--;
                continue;
            }
            vis[x][y]=1;
            for(int i=1;i<=n;i++) cnt+=1ll*a[x][i]*b[i][y],cnt%=mod;
            if(cnt!=c[x][y]){
				flg=0;
                break;
            }
        }
		
		if(flg){
			cout<<"Yes"<<'\n';
		}else{
			cout<<"No"<<'\n';
		}
	}
	return Elaina;
}

但当 n=3000 时,假设只有一个点是错误的,那么随到这个点的可能性实在太低。

于是你就想到了正解。但我没有

想到矩阵乘法的性质:
一个 \(n\times m\) 的矩阵 $\times $ 一个 \(m\times p\) 的矩阵 \(=\) 一个 \(n\times p\) 的矩阵

同理:
一个 \(n\times n\) 的矩阵 $\times $ 一个 \(n\times 1\) 的矩阵 \(=\) 一个 \(n\times 1\) 的矩阵

将题目改为 \(A\times B\times D = C\times D\),其中 \(D\) 为一个 \(n\times 1\) 的矩阵。

酱紫,就可以在 \(n^2\) 的复杂度下 check 完全部的点了。

矩阵乘不用我教你吧

int n,m;
int a[N][N],b[N][N],c[N][N];
long long d[N],e[N],f[N],g[N];

void cleanall(){
	for(int i=1;i<=n;i++){
		d[i]=e[i]=f[i]=g[i]=0;
		for(int j=1;j<=n;j++){
			a[i][j]=b[i][j]=c[i][j]=0;
		}
	}
}

signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	
	srand(time(0));
	int T;
	cin>>T;
	while(T--){
		cleanall();
		cin>>n;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				cin>>a[i][j];
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				cin>>b[i][j];
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				cin>>c[i][j];
			}
		}

		bool flg=1;
		
		for(int i=1;i<=n;i++) d[i]=rand();
        for(int i=1;i<=n;i++){
        	for(int j=1;j<=n;j++){
        		e[i]+=1ll*d[j]*a[j][i];
				e[i]%=mod;
			}
		}
		for(int i=1;i<=n;i++){
        	for(int j=1;j<=n;j++){
        		f[i]+=1ll*e[j]*b[j][i];
				f[i]%=mod;
			}
		}
		for(int i=1;i<=n;i++){
        	for(int j=1;j<=n;j++){
        		g[i]+=1ll*d[j]*c[j][i];
				g[i]%=mod;
			}
		}
        for(int i=1;i<=n;i++){
        	if(f[i]!=g[i]){
        		flg=0;
        		break;
			}
		}
		
		if(flg){
			cout<<"Yes"<<'\n';
		}else{
			cout<<"No"<<'\n';
		}
	}
	return Elaina;
}

T3 无标号 Multiset 构造

咕咕~

T4 有限制的构造

很巧妙的一个二维01背包,空间卡的死死的。

赛时糊了一个常规的二维背包,囍\(\color{purple}{RE}\)

正解:

正常DP显然TLE,由于值域很大,即使优化掉一维,我们也开不了f[A][B]那么大的数组,因此考虑间接推出答案。

\(k\) 道菜,甜度不超过 \(j\) 时最小的咸度,只要这个最小咸度小于 \(Y\),那么就可以吃这 \(k\) 道菜,最后寻找符合条件的最大值。

转移方程:

\[dp_{i,j,k}=min(dp_{i−1,j,k},dp_{i−1,j−A_i,k−1}+B_i) \]

可以倒序遍历优化掉一维。

int n,A,B,f[N][100];
struct NODE{
	int a,b;
}p[N];

signed main(){
	
	n=rd,A=rd,B=rd;
	for(int i=1;i<=n;i++){
		p[i].a=rd,p[i].b=rd;
	}
	
	
	memset(f,0x3f,sizeof(f));
	for(int i=0;i<=A;i++){
		f[i][0]=0;
	}
	for(int i=1;i<=n;i++){
		for(int j=A;j>=p[i].a;j--){
			for(int k=n;k;k--){
				f[j][k]=min(f[j][k],f[j-p[i].a][k-1]+p[i].b);
			}
		}
	}
	
	for(int i=n;i>0;i--){
		if(f[A][i]<=B){
			printf("%d",min(n,i+1));
			return 0;
		}
	}
	printf("1");
	return Elaina;
}
posted @ 2024-08-12 15:57  Elaina_0  阅读(20)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end