VK Cup 2017 - Round 3 部分题解

本文分享了VK Cup 2017 Round 3的比赛经历,重点解析了806D - Perishable Roads和806E - Blog Post Rating两道题目。对于Perishable Roads,通过转化问题为求最短路,利用Dijkstra算法求解。在Blog Post Rating题目中,通过挖掘数据特性,采用set和线段树进行分类讨论和状态维护,强调了解题过程中思维的细致和模型简化的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

VK Cup 2017 - Round 3

找到这套tourist出的题,非常有意思!

题解很详细

806D - Perishable Roads

有很多很巧的性质,最后转化出了这道题。利用它是一个完全图,并且答案是一条链,并且边权递减,转化成最短路。最后直接所有点一起跑dij,因为每个点的初始值可以为它的直接最优解。
积累思路:
1. 把最小权值的边的权值减去,最小边变为0. 统计答案的时候就很方便
2. 利用图的特殊性质,不断减小问题复杂度,从奇奇怪怪的生成树转化成最短路
#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) (x&(-x))

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const int inf = 1e9 + 10;
const int N = 6e6 + 10;
const int maxn = 3020;
const ll mod = 1e9 + 7;

int n,e[maxn][maxn],mn[maxn],allmn;
int vis[maxn];
ll ans;
ll dis[maxn];

void dij(){
	dis[0] = 1e18;
	while ( 1 ){
		int x = 0;
		rep(i,1,n) if ( !vis[i] && dis[i] < dis[x] ) x = i;
		if ( !x ) break;
		vis[x] = 1;
		rep(i,1,n){
			if ( x == i ) continue;
			dis[i] = min(dis[i],dis[x] + e[x][i]);
		}
	}
}
int main(){
	scanf("%d",&n);
	rep(i,1,n){
	
		rep(j,i + 1,n){
			scanf("%d",&e[i][j]) ,  e[j][i] = e[i][j];
		
		}
	
	}
	allmn = inf;
	rep(i,1,n){
		mn[i] = inf;
		rep(j,1,n){
			if ( i == j ) continue;
			mn[i] = min(mn[i],e[i][j]);
		}
		allmn = min(allmn,mn[i]);
	}
	rep(i,1,n){
	   	dis[i] = (mn[i] - allmn) * 2;
		rep(j,1,n){
			if ( i != j ) e[i][j] -= allmn;
		}
	}
	dij();
	rep(i,1,n) printf("%lld\n",dis[i] + (ll)allmn * (n - 1));
}

806E - Blog Post Rating

这同样是找性质的题
不仅要发现插入是按顺序的(这显然),并且-1和0,+1是分开的。不会有交叉。
每次插入只会使另外一个点变动,所以应该按照+1,-1分类讨论该怎么变。(不是一起想,这样脑袋会糊)
开3个set+两个线段树维护,set维护当前+1,0,-1的位置,线段树维护每个位置的答案和+1的时候和当前答案的差。相同数要想清楚细节。
细节还是挺多。要仔细检查,代码写错了很多地方,从开始写总共花了50min,更静心效率还可以更高。
可以积累的思路
数据结构题要简化模型,不断挖掘性质,不能对一个复杂的模型想笼统的用数据结构暴力维护。
写题中发现了更多没有想到的细节,说明思维远不够缜密,还要加油!
#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) (x&(-x))

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const int inf = 1e9 + 10;
const int N = 1e6 + 10;
const int maxn = 3020;
const ll mod = 1e9 + 7;

int sum[N << 2],mn[N << 2],tag[N << 2],add[N << 2];
int a[N],n,tmp = 5e5,L = 1e6;
multiset <int> s1,s2,s3;
multiset <int>::iterator it;
int ans;

inline void updates(int x){
	sum[x] = sum[x << 1] + sum[(x << 1) | 1];
}
int query(int x,int l,int r,int L,int R){
	if ( L <= l && R >= r ) return sum[x];
	int mid = (l + r) >> 1,res = 0;
	if ( L <= mid ) res += query(x << 1,l,mid,L,R);
	if ( R > mid ) res += query((x << 1) | 1,mid + 1,r,L,R);
	return res;
}
void modifys(int x,int l,int r,int id,int d){
	if ( l == r ){
		sum[x] += d;
		return;
	}
	int mid = (l + r) >> 1;
	if ( id <= mid ) modifys(x << 1,l,mid,id,d);
	else modifys((x << 1) | 1,mid + 1,r,id,d);
	updates(x);
}
inline void add_(int x,int d){
	add[x] += d;
	mn[x] += d;
}
inline void pushdown(int x){
	if ( add[x] ){
		if ( tag[x << 1] ) add_(x << 1,add[x]);
		if ( tag[(x << 1) | 1] ) add_((x << 1) | 1,add[x]);
		add[x] = 0;
	}
}
inline void update(int x){
	mn[x] = inf;
	if ( tag[x << 1] ) mn[x] = mn[x << 1];
	if ( tag[(x << 1) | 1] ) mn[x] = min(mn[x],mn[(x << 1) | 1]);
	if ( mn[x] != inf ) tag[x] = 1;
}
void modify(int x,int l,int r,int id,int d){
	if ( l == r ){
		if ( d ) tag[x] = 1 , mn[x] = d;
		else tag[x] = 0 , mn[x] = inf;
		return;
	}
	pushdown(x);
	int mid = (l + r) >> 1;
	if ( id <= mid ) modify(x << 1,l,mid,id,d);
	else modify((x << 1) | 1,mid + 1,r,id,d);
	update(x);
}
void modify(int x,int l,int r,int L,int R,int d){
	if ( !tag[x] || l > r ) return;
	if ( L <= l && R >= r ){ add_(x,d); return; }
	pushdown(x);
	int mid = (l + r) >> 1;
	if ( L <= mid ) modify(x << 1,l,mid,L,R,d);
	if ( R > mid ) modify((x << 1) | 1,mid + 1,r,L,R,d);
	update(x);
}
int queryL(int x,int l,int r,int L,int R){
	if ( !tag[x] || mn[x] > 1 ) return 0;
	if ( l == r ) return l;
	pushdown(x);
	int mid = (l + r) >> 1,ls = x << 1,rs = (x << 1) | 1;
	if ( L <= l && R >= r ){
		if ( mn[ls] == 1 ) return queryL(ls,l,mid,L,R);
		return queryL(rs,mid + 1,r,L,R);
	}
	int id = 0;
	if ( L <= mid ) id = queryL(ls,l,mid,L,R);
	if ( id ) return id;
	return queryL(rs,mid + 1,r,L,R);
}
int main(){
	freopen("input.txt","r",stdin);
	scanf("%d",&n);
	rep(i,1,n) scanf("%d",&a[i]);
	rep(i,1,n){
		int presum = query(1,0,L,0,a[i] + tmp);
		if ( a[i] < presum ){
			s1.insert(a[i]);
			it = s1.end();
			int d = *(--it);
			modifys(1,0,L,a[i] + tmp,-1);
			if ( d >= -(int)s1.size() + 1 ){
				s1.erase(it);
				s2.insert(d);
				modifys(1,0,L,d + tmp,1);
			}
			else{
				if ( s2.empty() ){
					modify(1,0,L,d + tmp,L,1);
					ans--;
				}
				else{
					it = s2.begin();
					modify(1,0,L,*it + tmp,1);
					modify(1,0,L,d + tmp,*it + tmp - 1,1);
					modifys(1,0,L,*it + tmp,1);
					s3.insert(*it);
					s2.erase(it);
				}
			}
		}
		else if ( a[i] > presum ){
			int id = queryL(1,0,L,a[i] + tmp,L);
			if ( !id ){
				ans++;
				modify(1,0,L,a[i] + tmp,a[i] - presum);
				modify(1,0,L,a[i] + tmp + 1,L,-1);
				modifys(1,0,L,a[i] + tmp,1);
			}
			else{
				it = s3.find(id - tmp);
				s2.insert(id - tmp);
				s3.erase(it);
				it = s3.find(id - tmp);
				if ( it == s3.end() ){
					modify(1,0,L,id,0);
				}
				modify(1,0,L,a[i] + tmp + 1,id - 1,-1);
				modify(1,0,L,a[i] + tmp,a[i] - presum);
				modifys(1,0,L,a[i] + tmp,1);
				modifys(1,0,L,id,-1);
			}
			s3.insert(a[i]);
		}
		else s2.insert(a[i]);
		printf("%d\n",ans);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值