2022 ZJNU暑期个人赛补题

11 篇文章 0 订阅
9 篇文章 0 订阅

Snow Boots II

题意:
有n块地板,每块地板有一定的积雪f,john有b双靴子,每双鞋子可以让他至多前进d,同时这双靴子让他最多只能在s的积雪中行走
john从1号地砖出发,他必须到达n号地砖,给出靴子的s和d,问这双靴子可以帮助他走完这段路吗?

数据范围:
1 ≤ \leq n , b n,b nb ≤ \leq 1 e 5 1e^5 1e5

想法:
先对靴子进行离线操作,对靴子的高度进行从大到小的顺序进行排序,同时把地板的积雪高也从大到小进行排序。

我们算最长不能通过的序列,这样的话,一点点把那些大于靴子的高度放进来,放进来之后,判断能否能前面的靴子或者后面的靴子相连(里面记一个数组,判断是否这双鞋已经被用到过了),判断是否相连的话可以用并查集,然后不断更新最大的不能通过的序列,就是要求的答案。

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 1e5 + 10;

int read() {
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}
int n, b;
struct node {
	int id, h;
}f[N];

struct shoe {
	int id, s, d;
}s[N];

bool cmp1(node a, node b) {
	return a.h > b.h;
}
bool cmp2(shoe a, shoe b) {
	return a.s > b.s;
}

int root[N];
int cross[N];
int color[N];

int find(int x) {
	if (root[x] == x) return x;
	else return root[x] = find(root[x]);
}

int ans[N];

void solve() {  
   
	cin >> n >> b;
	for (int i = 1; i <= n; i++) {
		cin >> f[i].h;
		f[i].id = i;
	}
	for (int i = 1; i <= b; i++) {
		cin >> s[i].s >> s[i].d;
		s[i].id = i;
	}
	sort(f + 1, f + 1 + n, cmp1);
	sort(s + 1, s + 1 + b, cmp2);

	for (int i = 1; i <= n; i++) {
		root[i] = i;
		cross[i] = 1;
	}

	int cnt = 1;
	int maxxcross = 0;
	for (int i = 1; i <= b; i++) {// 第i双靴子


	//	cout << s[i].s << " " << cnt << endl;
		while (cnt <= n && f[cnt].h > s[i].s) {
			

			color[f[cnt].id] = 1;//把这块地板染成黑的
			maxxcross = max(maxxcross, 1ll);
			if (color[f[cnt].id - 1]) {
				int x = find(f[cnt].id - 1);
				int y = find(f[cnt].id);
				root[x] = y;
				cross[y] += cross[x];

				maxxcross = max(maxxcross, cross[y]);
			}

			if (color[f[cnt].id + 1]) {
				int x = find(f[cnt].id);
				int y = find(f[cnt].id + 1);
				root[x] = y;
				cross[y] += cross[x];

				maxxcross = max(maxxcross, cross[y]);
			}
		//	cout << "%%%% " << f[cnt].id <<" "<<f[cnt].h << endl;
			cnt++;

			
		}
		if (maxxcross < s[i].d) {
			ans[s[i].id] = 1;
		}
	}
	for (int i = 1; i <= b; i++) {
		cout << ans[i] << endl;
	}

}

signed main() {

	ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);

	int t = 1;
	while (t--) {
		solve();
	}
	return 0;
}

Taming the Herd

题意:
奶牛出逃,在第一天一定会出逃,然后假如最近的出逃是3天前就计数器就为3,现在记录被改,问当发生i次出逃时,最少的修改次数(有点讲不明白

题目链接

数据范围:
1 ≤ \leq n n n ≤ \leq 100 100 100

思路:

求最小值可以用dp来写,用 d p [ i ] [ j ] dp[i][j] dp[i][j] 来表示到第i天奶牛出逃了j次的最小修改值,然后我们可以写一个n^3的dp

这里的 d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ k − 1 ] [ j − 1 ] + s u m [ k ] [ i ] ) dp[i][j]=min(dp[i][j],dp[k-1][j-1]+sum[k][i]) dp[i][j]=min(dp[i][j],dp[k1][j1]+sum[k][i])
里面的sum[k][i]是从k到i需要修改多少次才能变成从k那天开始出逃,sum数组可以通过预处理处理出来

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define INF 0x3f3f3f3f

#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
#define mst(a, b) memset(a, b, sizeof(a))

const int N = 520;


int a[N];
int sum[N][N];
int dp[N][N];

void solve() {  
   
	int n;
	cin >> n;
	rep(i, 1, n) cin >> a[i];
	
	rep(i, 1, n) {
		int cnt = 0;
		rep(j, i, n) {
			if (a[j] != j - i)cnt++;
			sum[i][j] = cnt;
		}
	}
	mst(dp, INF);
	rep(i, 0, n)dp[0][i] = 0;
	rep(i, 1, n) {
		rep(j, 1, i) {
			rep(k, j, i) {
				dp[i][j] = min(dp[i][j], dp[k - 1][j - 1] + sum[k][i]);
			}
		}
	}

	rep(i, 1, n) {
		cout << dp[n][i] << endl;
	}

}

signed main() {

	ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);

	int t = 1;
	while (t--) {
		solve();
	}
	return 0;
}

First!

题意:
给了一堆字符串,问是否有方法可以通过重新排列字典序让当前字符串排在第一个

数据范围:
1 ≤ \leq n n n ≤ \leq 3 e 5 3e^5 3e5

想法:和字符串字典序有关的话,先建一颗tire树
如果一个字符串能够重新规划后得到字典序最小的那个,那它在每一层上面都要大于其他节点,所有我们要一层层的往下进行搜索,把其他的结点看做一个点,建一条边
最多判断我们建出来的图是不是有环就可以了
判环的话可以用拓扑排序

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'


const int N = 3e5 + 100;
int read() {
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}


int trie[N][26];
int id;
int cnt[N];

void insert(string s) {
	int p = 0;
	for (int i = 0; i < s.size(); i++) {
		int x = s[i] - 'a';
		if (trie[p][x] == 0) trie[p][x] = ++id;
		p = trie[p][x];
	}
	cnt[p]++;
}
int find(string s) {
	int p = 0;
	for (int i = 0; i < s.size(); i++) {
		int x = s[i] - 'a';
		if (trie[p][x] == 0) return 0;
		p = trie[p][x];
	}
	return cnt[p];
}

string s[N];
int ans[N];
int ans_sum;
vector<int>e[27];
int ind[30];

int work(string s) {
	int p = 0;
	for (int i = 0; i < s.size(); i++) {
		int x = s[i] - 'a';
		if (cnt[p]) return 0;

		for (int j = 0; j < 26; j++) {
			if (j != x && trie[p][j]) {
				e[x].push_back(j);
				ind[j]++;
			}
		}
		p = trie[p][x];
	}
	return 1;
}

bool check() {
	queue<int>q;
	for (int i = 0; i < 26; i++) {
		if (!ind[i])q.push(i);
	}
	while (!q.empty()) {
		int u = q.front();q.pop();
		for (int i = 0; i < e[u].size(); i++) {
			ind[e[u][i]]--;
			if (!ind[e[u][i]]) q.push(e[u][i]);
		}
	}
	for (int i = 0; i < 26; i++) {
		if (ind[i]) return 0;
	}
	return 1;
}

void solve() {	
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> s[i];
		insert(s[i]);
	}
	for (int i = 1; i <= n; i++) {
		memset(ind, 0, sizeof(ind));
		for (int j = 0; j < 27; j++)e[j].clear();
		if (!work(s[i])) continue;
		if (check()) ans[++ans_sum] = i;
	}
	cout << ans_sum << endl;
	for (int i = 1; i <= ans_sum; i++) {
		cout << s[ans[i]] << endl;
	}
}


signed main() {

	ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);

	int t = 1;
	while (t--) {
		solve();
	}
	return 0;
}

DLAKAVAC

题意:

刚开始会有一批人被感染,接下来每天ID为p的人将会被感染,当且仅当ID为a的人在前一天被感染了,且0号病人里面存在ID为b的一个人,并且还要满足( a * b )mod M = p
这个式子
问第k天,有哪些人被感染了

数据范围:
1 ≤ \leq k k k ≤ \leq 1 e 18 1e^{18} 1e18 , 1 ≤ \leq M M M ≤ \leq 1500 1500 1500

思路:通过打表可以发现里面存在着循环节,所以直接用map<set,int>开始记忆判断就可以了

#include<bits/stdc++.h>

using namespace std;

#define int long long
#define endl '\n'
const int mod = 1e9 + 7;

#define ll long long

const int N = 200000;
bool vis[N];
vector<int>id, idd;


map<set<int>, int>mp;
map<int, set<int>>ans;

vector<int> tmr;
int n;
set<int>se;
int cnt;

signed main() {
    int K, M, N;
	cin >> K >> M >> N;
	for (int i = 0; i <= M; i++) vis[i] = 0;
	for (int i = 1; i <= N; i++) {
		int x;
		cin >> x;
		if (vis[x]) continue;
		vis[x] = 1;
		idd.push_back(x);
		id.push_back(x);
		se.insert(x);
	}
	mp[se] = ++cnt;
	ans[cnt] = se;
	int now = 1;
	N = idd.size();
	while (1) {
		n = id.size();
		for (int i = 0; i <= M; i++) vis[i] = 0;
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < n; j++) {
				int tp = idd[i] % M * id[j] % M;
				//cout<<"*****"<<idd[i]<<" "<<id[j]<<" "<<tp<<endl;
				if (!vis[tp]) {
					tmr.push_back(tp);
					vis[tp] = 1;
				}
			}
		}
		sort(tmr.begin(), tmr.end());
		id.clear();
		n = tmr.size();
		se.clear();
		for (int i = 0; i < n; i++) {
			id.push_back(tmr[i]);
			se.insert(tmr[i]);
		}
		//cout<<endl;
		if (mp[se]) {
			now = mp[se];
			break;
		}
		else mp[se] = ++cnt;

		ans[cnt] = se;
		tmr.clear();
	}
	//cout<<now<<" "<<cnt<<endl;
	if (n == 0) {
		cout << 0 << endl;
	}
	else if (K <= now) {
		for (auto x : ans[K]) {
			cout << x << ' ';
		}
		cout << endl;
	}
	else {
		int x = now + (K - now) % (cnt - now + 1);
		x = min(x, cnt);
		for (auto x : ans[x]) {
			cout << x << " ";
		}
		cout << endl;
	}  
    return 0;
}

Fort Moo

题意:造堡垒,寻找最大的矩形框架,且框架不可以造在沼泽上

数据范围:
1 ≤ \leq n n n ≤ \leq 200 200 200

思路:用前缀和进行预处理,然后n^2枚举两条支架,用最后一层n来判断是否可以在这两条支架里面进行搭建

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=205;
int n,m,b[N][N];
char a[N][N];
inline void init(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++){
		scanf("%s",a[i]+1);
	}
	for (int j=1;j<=m;j++){
		for (int i=1;i<=n;i++){
			if (a[i][j]=='X') b[i][j]=b[i-1][j]+1;
				else b[i][j]=b[i-1][j];
		}
	}
}
int ans;
inline void solve(){
	for (int i=1;i<=n;i++){
		for (int j=i;j<=n;j++){
			int x=0,y=0;
			for (int k=1;k<=m;k++){
				if (b[j][k]-b[i-1][k]==0){
					x=max(x,k);
					y=x;
					while (x<m&&a[i][x+1]=='.'&&a[j][x+1]=='.'){
						x++;
						if (b[j][x]-b[i-1][x]==0) y=x;
					}
					ans=max(ans,(j-i+1)*(y-k+1));
				}
			}
		}
	}
	printf("%d\n",ans);
}
int main(){
	init();
	solve();
	return 0;
} 

Redistributing Gifts

题意:
每头奶牛都有一个愿望单,是所有n个礼物的一个排列,奶牛相对序列中较晚出现的礼物更喜欢序列中较早出现的礼物。对于1到n中的每一个,计算奶牛i在重新分配后更有希望收到的最喜欢的礼物。

思路:传递闭包 ,套一个板子就行了

#include<bits/stdc++.h>

using namespace std;
typedef unsigned long long ull;


const int mod = 1e9 + 7;


const int N = 1e5 + 10;

int a[520][520];
int dis[520][520];

void solve() {
    int n;
    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++) {
            dis[i][a[i][j]] = 1;
            if (a[i][j] == i) break;
        }
    }
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                dis[i][j] |= (dis[i][k] & dis[k][j]);
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (dis[i][a[i][j]] && dis[a[i][j]][i]) {
                cout << a[i][j] << endl;
                break;
            }
        }
    }

}

signed main() {
    int t;
    t = 1;
    while (t--) {
        solve();
    }
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值