【学习笔记】NOIP暴零赛

过路费

原题 CodeForces gym 101630 J
经典的错算例子。

如果我们只支付 ≥ m i d \ge mid mid边的费用,那么 < m i d <mid <mid的边边权是 0 0 0,可以随便经过。

因此我们枚举边权,把 w w w改为 max ⁡ ( 0 , w − m i d ) \max(0,w-mid) max(0,wmid),然后跑最短路,答案是 d i s [ n ] + k ∗ m i d dis[n]+k*mid dis[n]+kmid

如果经过的 ≥ m i d \ge mid mid的边超过 k k k的话,那么算出来比答案大,原因在于对 > k >k >k条边支付了费用。

如果经过的 ≥ m i d \ge mid mid的边不足 k k k的话,那么算出来还是比答案大

因此如果对答案有贡献,那么经过的 ≥ m i d \ge mid mid的边一定恰好是 k k k

如果本身经过的边就不足 k k k条,那么让 m i d = 0 mid=0 mid=0即可。

#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define pb push_back
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N=3005;
//自闭 
int n,m,K,vis[N];
int head[N<<2],nxt[N<<2],to[N<<2],w[N<<2],tot;
ll dis[N],res(inf);
void add(int x,int y,int z){
	to[++tot]=y,w[tot]=z,nxt[tot]=head[x],head[x]=tot;
}
priority_queue<pair<ll,int>>Q;
struct node{
	int u,v,w;
}e[3005];
void dij(){
	dis[1]=0,Q.push({0,1});
	while(Q.size()){
		int u=Q.top().se;Q.pop();if(vis[u])continue;vis[u]=1;
		for(int k=head[u];k;k=nxt[k]){
			int v=to[k],W=w[k];
			if(dis[u]+W<dis[v])dis[v]=dis[u]+W,Q.push({-dis[v],v});
		}
	}
}
ll solve(int mid){
	for(int i=1;i<=n;i++)head[i]=0,vis[i]=0,dis[i]=inf;tot=0;
	for(int i=1;i<=m;i++){
		int u=e[i].u,v=e[i].v,w=e[i].w;
		add(u,v,max(0,w-mid)),add(v,u,max(0,w-mid));
	}dij();return dis[n]+(ll)K*mid;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>K;
	for(int i=1;i<=m;i++){
		int u,v,w;cin>>u>>v>>w,e[i]={u,v,w};
	}
	res=solve(0);
	for(int i=1;i<=m;i++){
		res=min(res,solve(e[i].w));
	}
	cout<<res;
}

搞破坏

原题 [APIO2017] 斑斓之地

平面图中的欧拉定理:

设G为任意的联通平面图,则v-e+f=2,v是G的顶点数,e是G的边数,f是G的面数。

可以简单理解为,首先建一棵树,然后每新增一条边,就会多分割出一个平面。

对于每两块相邻的可用格子,连一条边,形成一个图G。题目就是问矩形区域内导出子图中连通块个数。

网格图显然是平面图。因此v-e+f=连通块数+1。

e就是1x2格子的个数,f就是2x2格子的个数+1,v就是1x1格子的个数。可以假想刚开始所有格子都是可用的,然后删掉一个格子后的变化,可以二维数点解决。

注意如果询问的矩形区域包含怪兽经过的所有格子,那么怪兽经过的所有格子的外围一圈格子会形成一个新的面,特判即可。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

似乎t3,t4不难,但是我是丝薄。我只会打暴力。

感觉效率好低啊

其实写博客也算一种放松吧

#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define pb push_back
#define db double
#define mp make_pair
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
int R,C,n,m,X,Y;
int X3,X4,Y3,Y4;
int bit[200005][5];
ll res[100005];
map<pair<int,int>,int>id;
struct node{
	int x,type,id,val;
};
struct node2{
	int x,type;
};
vector<node>v[200005];
vector<node2>g[200005];
string s;
void add(int x,int y,int type){
	if(!x||!y)return;
    g[x].pb({y,type});
}
void Add(int x,int y,int type,int id,int val){
	if(!x||!y)return;
	v[x].pb({y,type,id,val});
}
void add2(int X,int Y,int X2,int Y2,int type,int id,int val){
	if(X>X2||Y>Y2)return; 
	Add(X2,Y2,type,id,val),Add(X-1,Y2,type,id,-val),Add(X2,Y-1,type,id,-val),Add(X-1,Y-1,type,id,val);
}
int V(int x,int y){
	return id.find(mp(x,y))==id.end();
}
void del(int x,int y){
	if(!V(x,y))return;
	add(x,y,1);
	if(V(x,y+1))add(x,y,2);
	if(V(x,y-1))add(x,y-1,2);
	if(V(x+1,y))add(x,y,3);
	if(V(x-1,y))add(x-1,y,3);
	if(V(x-1,y-1)&&V(x-1,y)&&V(x,y-1)&&V(x,y))add(x-1,y-1,4);
	if(V(x-1,y)&&V(x-1,y+1)&&V(x,y)&&V(x,y+1))add(x-1,y,4);
	if(V(x,y)&&V(x,y+1)&&V(x+1,y)&&V(x+1,y+1))add(x,y,4);
	if(V(x,y-1)&&V(x,y)&&V(x+1,y-1)&&V(x+1,y))add(x,y-1,4);
	id[mp(x,y)]=1;
}
void upd(int x,int type,int z){for(;x<=C;x+=x&-x)bit[x][type]+=z;}
int ask(int x,int type){int tot(0);for(;x;x-=x&-x)tot+=bit[x][type];return tot;}
int main(){
//	freopen("data.in","r",stdin);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>R>>C>>n>>m>>X>>Y;del(X,Y);if(n)cin>>s;X3=X4=X,Y3=Y4=Y;
	for(int i=0;i<n;i++){
		if(s[i]=='N')X--;else if(s[i]=='S')X++;
		else if(s[i]=='W')Y--;else Y++;
		X3=min(X3,X),X4=max(X4,X),Y3=min(Y3,Y),Y4=max(Y4,Y);
		del(X,Y);
	}
	for(int i=1;i<=m;i++){
		int X,Y,X2,Y2;
		cin>>X>>Y>>X2>>Y2;
		if(X<X3&&X4<X2&&Y<Y3&&Y4<Y2)res[i]++; 
		add2(X,Y,X2,Y2,1,i,-1),add2(X,Y,X2,Y2-1,2,i,1),add2(X,Y,X2-1,Y2,3,i,1),add2(X,Y,X2-1,Y2-1,4,i,-1);
	}for(int i=1;i<=R;i++){
		for(auto x:g[i])upd(x.x,x.type,1);
		for(auto x:v[i]){
			res[x.id]+=x.val*ask(x.x,x.type);
		}
	}for(int i=1;i<=m;i++){
		cout<<res[i]+1<<"\n";
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值