「模拟赛」CSP提高组模拟1(7.14)

和新高一的学长弟弟们一起打的模拟赛

第一次在新 oj 网站 h.hszxoj 上打模拟赛,功能多了不少
但是没有替换 ONLINE_JUDGE,导致赛时有四个人(包括我)因为写了这几句代码而爆零,悲!
下文题目后所跟为应得分数

	#ifdef ONLINE_JUDGE
	freopen("...");
	#endif

A . 最短路

80pts

较水,求多源最短路,只不过定义一点到另一点的的权值为路径上的点权最大值与路径上所有边权之和。

考察对 floyd 的理解,在普通 floyd 的基础上加点东西就行。不过只能得 80pts,跑两遍就 A 了。

code:

#include<bits/stdc++.h>
using namespace std;

const int N = 3010;

int n, m, q;
int val[N], vax[N][N];
long long dis[N][N];

void yuen(){
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++){
			dis[i][j] = vax[i][j] = -1;
		}
	}
}

void floyd_yuen(){
	for(int k=1; k<=n; k++){
		for(int i=1; i<=n; i++){
			for(int j=1; j<=n; j++){
				if((dis[i][j] <= 0 or dis[i][j] + vax[i][j] > dis[i][k] + dis[k][j] + max(vax[i][k], vax[k][j]))
				and dis[k][j] > 0 and dis[i][k] > 0){
					vax[i][j] = max(vax[i][k], vax[k][j]);
					dis[i][j] = dis[i][k] + dis[k][j];
				}
			}
		}
	}
	
}

int main(){
	freopen("path.in", "r", stdin); freopen("path.out", "w", stdout);

	std::cin>>n>>m>>q;
	yuen();
	for(int i=1; i<=n; i++){
		std::cin>>val[i];
		dis[i][i] = 0, vax[i][i] = val[i];
	}

	for(int i=1; i<=m; i++){
		int x, y, z;
		std::cin>>x>>y>>z;
		vax[y][x] = vax[x][y] = max(val[x], val[y]);
		if(dis[x][y] == -1 or dis[x][y] > z){
			dis[x][y] = dis[y][x] = z;
		}
	}

	floyd_yuen(); floyd_yuen();

	while(q--)
	{
		int s, t;
		std::cin>>s>>t;
		if(dis[s][t] < 0) cout<<"-1\n";
		else cout<<dis[s][t]+vax[s][t]<<endl;
	}


	return 0;
}

B . 方格取数

15pts

给定一个由正整数组成的 𝑛 ∗ 𝑛 大小的矩阵和一个正整数 𝑘,你需要取出一个子矩形,使得矩形内的数之和 𝑆 满足 𝑆 ∈ [𝑘,2𝑘],如果有多组解,你可以输出任意一个。

赛时没想到好做法,最后求了个而为前缀和打了个暴力上去,赛后知道 大黄 求前缀后的时候特判就拿了 85 pts..... oj题库样例过于水了。

正解:

先求个二维前缀和方便计算子矩形面积。并把所有 \(>2*k\) 的点都置为零,因为只要这个点 \(>2*k\),那么含有这个点的矩阵一定不可能合法(点值都为正)。当然,若果遇到大小在 \([k, 2*k]\) 间的直接就是答案了。

所以剩下的点就只有 \(<k\) 的了。

在保证所有点都 \(<k\) 之后我们可知道这些性质:

  • 如果一个矩形 \(>2*k\),那么这个矩形之中一定存在一个满足条件的矩形。

    我们把这个矩形一行一行地删除,每删除新的一行后,有以下三种情况:
    1.如果该矩形仍然 \(>2*k\),继续删除;
    2.这个矩形在 \([k, 2*k]\) 间,即为答案;
    3.这个矩形 \(<k\) 时,那么在删除的那一行中一定存在答案,我们只需要在删除的那一行中一个块一个块地删直到改行满足答案条件即可。

  • 如果一个矩形 \(<k\),该矩形中不存在答案。

所以我们求出来最大的合法矩形(合法即为矩形中不包含最开始操作置为 \(0\) 的点),若该矩形 \(<k\),那么不存在答案,输出 "-1";在 \([k, 2*k]\)之间,直接是答案;否则进行上述删除操作找出答案。

对于如何求最大的合法矩形:

悬线法和单调队列。

悬线法枚举下限,我们用单调队列(即 广告印刷 的求法)求每一个下限以上的合法矩形找最大即可。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 4010;

int n, k;
int a[N][N], h[N];
ll sum[N][N];

ll getsize(int x1, int y1, int x2, int y2){
	return sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1];
}

signed main(){
	freopen("matrix.in", "r", stdin); freopen("matrix.out", "w", stdout);

	scanf("%d%d", &n, &k);
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++){
			scanf("%d", &a[i][j]);
			sum[i][j] = sum[i][j-1] + a[i][j];
			if(a[i][j] > 2 * k) a[i][j] = 0;
		}
	}
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++){
			sum[j][i] += sum[j-1][i];
		}
	}

	ll maxt = 0;
	int nowid = 0, nowup = 0, nowsh = 0, now = 0;
	for(int up=1; up<=n; up++){	
		deque<int>q; q.clear();

		for(int j=1; j<=n+1; j++){
			if(!a[up][j]){ h[j] = 0;}
			else h[j]++;

			while(q.size() and h[q.front()] > h[j])
			{
				ll size = getsize(up-h[q.front()]+1, q.front(), up, j-1);

				if(size >= k and size <= 2 * k){
					printf("%d %d %d %d\n", up-h[q.front()]+1, q.front(), up, j-1);
					return 0;
				}
				if(size > maxt){
					maxt = size; nowsh = nowup + h[q.front()] - 1;
					nowid = q.front(); nowup = up; now = j - 1;
				}
				q.pop_front();
			}
			
			q.push_front(j);
		}
	}

	if(maxt < k){cout<<"-1"; return 0; }

	for(int i=nowsh; i>=nowup; i--){
		ll size = getsize(nowup, now, i, now);

		if(size > 2 * k) continue;
		if(size < k){
			for(int j=nowid; j<=now; j++){
				ll area = getsize(i+1, nowid, i+1, j);
				if(area >= k and area <= 2 * k){
					printf("%d %d %d %d\n", i+1, nowid, i+1, j);
					return 0;
				}
			} 
		}
		else{
			printf("%d %d %d %d\n", nowup, now, i, now);
			return 0;
		}
	}

	return 0;
}

C . 数组

给定一个长度为 𝑛 的数组,第 𝑖 项为𝑎iai​,你需要支持以下操作: 1,给定l,r,𝑥,将区间 [𝑙,𝑟] 中所有元素乘 𝑥。 2. 给定 𝑙,𝑟,查询区间 [𝑙,𝑟] 中所有元素之积的欧拉函数值,因为结果可能很大,你只需要输出答案对 \(10^9+ 7\) 取模的结果。 你共需进行 𝑞 次操作。

0 pts。赛时关于欧拉函数的性质,求法啥的都忘了,只粘了一个求单个数的欧拉值的模板,用处不大,其实是完全没用。

正解是线段树,显然时间复杂度为 $O(log {\large{n}} ) $。我打的为数不多的分块,可做。

code:

#include<bits/stdc++.h>
#define Aqr puts("\n\n------------");
#define yun puts("\n------------\n\n");
#define ll long long
#define int long long
using namespace std;

const int N = 1e3 + 10;
const int mod = 1e9 + 7;

int n, q; ll f[65];
int va[100005], vb[100005], belong[1000005], inv[305], prime[65];
int st[N], en[N], ans[N], size[N], lazy[N], tag[N], tall[N];

int qpow(int x, int b){
	ll res = 1, a = x;
	while(b)
	{
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

void pre(){
	int cnt = 0;
	for(int i=2; i<=300; i++){
		bool flag = false;
		for(int j=2; j<=sqrt(i); j++)
			if(i % j == 0){flag = true; break;}
		if(flag) continue;
		prime[cnt++] = i;
		// cout<<i<<" ";
	}
	inv[1] = 1;
	for(int i=2; i<=300; i++)
		inv[i] = mod - (mod / i) * inv[mod % i] % mod;
	for(int i=0; i<62; i++){
		f[i] = inv[prime[i]] * (prime[i] - 1) % mod;
	}
}

void yuen(){
	int sq = sqrt(n);
	for(int i=1; i<=sq; i++){
		st[i] = sq * (i - 1) + 1;
		en[i] = sq * i;
		ans[i] = lazy[i] = 1;
	}
	if(en[sq] < n) en[sq] = n;

	for(int i=1; i<=sq; i++){
		for(int j=st[i]; j<=en[i]; j++){
			belong[j] = i;
			ans[i] = ans[i] * va[j] % mod;
			tall[i] |= vb[j];
		}
		size[i] = en[i] - st[i] + 1;
	}
}

void update(int l, int r, int k, int pi){
	if(belong[l] == belong[r]){
		tall[belong[l]] |= pi;
		int res = qpow(k, r - l + 1);
		ans[belong[l]] = ans[belong[l]] * res % mod;

		for(int i=l; i<=r; i++){
			va[i] = va[i] * k % mod;
			vb[i] |= pi;
		}
		
	}
	else{
		int res = qpow(k, en[belong[l]] - l + 1);
		ans[belong[l]] = (ll)ans[belong[l]] * res % mod;
		tall[belong[l]] |= pi;

		res = qpow(k, r - st[belong[r]] + 1);
		ans[belong[r]] = (ll)ans[belong[r]] * res % mod;
		tall[belong[r]] |= pi;
		
		for(int i=l; i<=en[belong[l]]; i++){
			va[i] = va[i] * k % mod;
			vb[i] |= pi;
		}
		
		for(int i=st[belong[r]]; i<=r; i++){
			va[i] = va[i] * k % mod;
			vb[i] |= pi;
		}
			
		for(int i=belong[l]+1; i<belong[r]; i++){
			ans[i] = (ll)ans[i] * qpow(k, size[i]) % mod;
			lazy[i] = (ll)lazy[i] * k % mod;
			tag[i] |= pi; tall[i] |= pi;
		}
	}
}

int query(int l, int r){
	if(belong[l] == belong[r]){
		int res = 1, t = tag[belong[l]];
		for(int i=l; i<=r; i++){
			res = res * va[i] % mod * lazy[belong[l]] % mod;
			t |= vb[i];
		}
		for(int i=0; i<62; i++){
			if(t & (1ll << i))
			res = res * f[i] % mod;
		}
		return res;
	}
	else{
		int res = 1, t = 0;

		t |= tag[belong[l]];
		for(int i=l; i<=en[belong[l]]; i++){
			t |= vb[i];
			res = (ll)res * va[i] % mod * lazy[belong[i]] % mod;
		}
		
		t |= tag[belong[r]];
		for(int i=st[belong[r]]; i<=r; i++){
			t |= vb[i];
			res = (ll)res * va[i] % mod * lazy[belong[i]] % mod;
		}
		
		for(int i=belong[l]+1; i<belong[r]; i++){
			res = (ll)res * ans[i] % mod;
			t |= tall[i];
		}

		for(int i=0; i<62; i++)
			if(t & (1ll << i)) res = res * f[i] % mod;
		
		return res;
	}
}

signed main(){
	freopen("array.in", "r", stdin); freopen("array.out", "w", stdout);

	// #ifndef ONLINE_JUDGE
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
	// #endif

	pre();

	scanf("%lld%lld", &n, &q);
	for(int i=1; i<=n; i++){
		int sa; scanf("%lld", &sa);
		va[i] = sa;
		for(int j=0; j<62; j++){
			if(prime[j] > sa) break;
			if(sa % prime[j] == 0) vb[i] |= (1ll << j);
		}
	}

	yuen();

	while(q--)
	{
		int op;
		scanf("%lld", &op);
		if(op == 1){
			int l, r, x; scanf("%lld%lld%lld", &l, &r, &x);
			int can = 0;
			for(int i=0; i<62; i++){
				if(x % prime[i] == 0) can |= (1ll << i);
			}
			update(l, r, x, can);
		}
		else{
			int l, r; scanf("%lld%lld", &l, &r);
			printf("%lld\n", query(l ,r));
		}
	}

	return 0;
}
posted @ 2024-07-14 10:19  Aqr_Rn  阅读(10)  评论(0编辑  收藏  举报