HGOI11.8集训题解

题解

今天是暴力局嘛…


第一题——玩具装箱——(toy)

【题目描述】

  • 一些玩具从左开始依次装成若干箱,每个箱子只能装最多 M M M个且编号连续的玩具。在某个箱子里装若干个玩具的费用计算方法:如果该箱子里最大的玩具为 a a a,最小的为 b b b,玩具个数为 s s s,则费用为 K + s ∗ ( a − b ) K+s*(a-b) K+s(ab)
  • K K K是箱子本身的费用,所有箱子的费用都相等。
  • 求最小费用

  • 显然可以得出方程 f [ i ] = m i n { f [ j − 1 ] + ( i − j + 1 ) ∗ ( m a x j , i − m i n j , i ) } + K f[i]=min\left\{f[j-1]+(i-j+1)*(max_{j,i}-min_{j,i})\right\}+K f[i]=min{f[j1]+(ij+1)(maxj,iminj,i)}+K
  • 然后数据很水, O ( n m ) O(nm) O(nm)水过
  • 期望得分100,实际得分100

#include <bits/stdc++.h>
#include <stdio.h>
#define LL long long
using namespace std;
void fff(){
	freopen("toy.in","r",stdin);
	freopen("toy.out","w",stdout);
}
LL read(){
	LL x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	return x;
}
const LL INF=0x3f3f3f3f;
const int N=20100;
const int M=1010;
LL st_max[N][21],st_min[N][21];
LL h[N],a[N],f[N];
LL n,m,k;
LL query_max(int l,int r){
	LL kk=h[r-l+1];
	return max(st_max[l][kk],st_max[r-(1<<kk)+1][kk]);
}
LL query_min(int l,int r){
	LL kk=h[r-l+1];
	return min(st_min[l][kk],st_min[r-(1<<kk)+1][kk]);
}
int main(){
//	fff();
	n=read(),m=read(),k=read();
	for(int i=1;i<=n;i++)a[i]=read(),st_max[i][0]=st_min[i][0]=a[i];
	for(int i=1;i<=n;i++){
		h[i]=h[i-1];
		if((1<<(h[i]+1)<=i)) h[i]++;
	}
	for(int j=1;j<=20;j++){
		for(int i=1;i+(1<<j)-1<=n;i++){
			st_max[i][j]=max(st_max[i][j-1],st_max[i+(1<<(j-1))][j-1]);
			st_min[i][j]=min(st_min[i][j-1],st_min[i+(1<<(j-1))][j-1]);
		}
	}
	memset(f,INF,sizeof(f));
	f[0]=0;
	for(int i=1;i<=n;i++){
		for(int j=max(1ll,i-m+1);j<=i;j++){
			f[i]=min(f[i],f[j-1]+k+(i-j+1)*(query_max(j,i)-query_min(j,i)));
		}
	}
	printf("%lld",f[n]);
}

第二题——铁路运费(train)

【题目描述】

  • 给出一张n个点m条边的无向图,边权为1,进行Q次操作,每次操作对之后结果有影响
  • 每次操作将某一条边的边权加到2
  • 问每次操作之后到1的最短路相对于原图有变化的节点有多少

  • 显然,这个题暴力分数有50分可以拿。打个暴力绝对不亏!
  • 然后,发现每次改变边权就是在删边啊!但是删边不怎么好做
  • 那就把这个删边倒过来,变成加边,然后就询问就好了啊…

#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
#define FILENAME "train"
const int MAX_N = 100000;
const int MAX_M = 200000;
vector<int> G[MAX_N];
int dis[MAX_N];
int N,Q;
void bfs_all(int s){
	for(int i = 0; i < N; i++) dis[i] = -1;
	dis[s] = 0;
	queue<int> que;
	que.push(s);
	while(!que.empty()){
		int v = que.front();
		que.pop();
		for(int i = 0; i < G[v].size(); ++i){
			int u = G[v][i];
			if(dis[u] != -1 && dis[u] <= dis[v] + 1) continue;
			dis[u] = dis[v] + 1;
			que.push(u);
		}
	}
}
bool ok[MAX_N];
vector<int> G2[MAX_N];
int bfs(int u, int v){
	if(dis[u] == dis[v])return 0;
	if(dis[u] > dis[v]) swap(u, v);
	G2[u].push_back(v);
	int res = 0;
	if(ok[u]){
		queue<int> que;
		que.push(v);
		while(!que.empty()){
			int v = que.front();
			que.pop();
			if(ok[v]) continue;
			ok[v] = true;
			res++;
			for(int i = 0; i < G2[v].size(); ++i){
				int u = G2[v][i];
				que.push(u);
			}
		}
	}
	return res;
}
int M,qs[MAX_M],us[MAX_M],vs[MAX_M],ord[MAX_M];
bool used[MAX_M];
void init(){
	for(int i = 0; i < M; ++i)used[i] = false;
	for(int q = 0; q < Q; ++q){
		ord[q] = qs[q];
		used[qs[q]] = true;
	}
	int c = Q;
	for(int i = 0; i < M; ++i)
		if(!used[i])
			ord[c++] = i;
}
int cnt[MAX_M + 1];
int main(){
//   freopen(FILENAME ".in", "r", stdin);
//   freopen(FILENAME ".out", "w", stdout); 
	scanf("%d%d%d", &N, &M, &Q);
	for(int i = 0; i < M; ++i){
		scanf("%d%d", us + i, vs + i);
		us[i]--;	vs[i]--;
		G[us[i]].push_back(vs[i]);
		G[vs[i]].push_back(us[i]);
	}
	bfs_all(0);
	for(int q = 0; q < Q; ++q){
		scanf("%d", qs + q);
		qs[q]--;
	}
	init();
	int prv = 0;
	ok[0] = true;
	cnt[M] = 0;
	for(int i = M - 1; i >= 0; --i){
		int u = us[ord[i]], v = vs[ord[i]];
		int cur = bfs(u, v);
		cnt[i] = prv + cur;
		prv = cnt[i];
	}
	for(int q = 0; q < Q; ++q)
		printf("%d\n", N - 1 - cnt[q + 1]);
	return 0;
}


第三题——模式(patterns)

【题目描述】

  • 给出一段序列,求出最长的出现次数超过k的相同的子串

  • 这个暴力hash+map
  • 期望得分100

#include <bits/stdc++.h>
#include <stdio.h>
#define LL long long
#define ull unsigned long long
using namespace std;
void fff(){
	freopen("patterns.in","r",stdin);
	freopen("patterns.out","w",stdout);
}
int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	return x;
}
const int N=21000;
const ull base1=233;
int n,k;
int a[N],nxt[N];

ull Hash[N],Pow[N];
ull HASH(int l,int len){
	int r=l+len-1;
 	return Hash[r]-Hash[l-1]*Pow[r-l+1];
}
map<ull,int> mp;
bool solve(int len){
	mp.clear();
	for(int i=1;i+len-1<=n;i++){
		ull tt=HASH(i,len);
		mp[tt]++;
		if(mp[tt]>=k) return true;
	}
	return false;
}
int main(){
//	fff();
	n=read(),k=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for (int i=1;i<=n;i++) Hash[i]=Hash[i-1]*base1+a[i];
	Pow[0]=1; 
	for (int i=1;i<=n;i++) Pow[i]=Pow[i-1]*base1;
	if(n<=40){
		for(int len=n-1;len>=1;len--){
			for(int begin=1;begin+len-1<=n;begin++){
				ull tar=HASH(begin,len);int cnt=0;
				for(int i=begin+1;i+len-1<=n;i++){
					if(HASH(i,len)==tar) cnt++;
					if(cnt>=k-1){
						printf("%d",len);
						return 0;
					}
				}
			}
		}
	}else{
		
		int l=1,r=n-1;
		int ans;
		while(l<=r){
			int mid=(l+r)>>1;
			if(solve(mid)) l=mid+1,ans=mid;
			else r=mid-1;
		}
//		while(!solve(l)) l--;
		printf("%d",ans);
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值