杭电多校2020第二场

1001-Total Eclipse

题意:
n座城市,m条路,每个城市有一盏亮度为bi的灯,每次可以选择一个联通块让联通块上所有城市k(k的数量要尽可能大)的灯亮度减1,但灯亮度为0的城市不能算入联通块中,问让全部城市的灯亮度变为0的最小次数是多少。

题解:
(这题偷偷改过题面也没发通知==,加了k要尽量大这句话)
我们可以找某个联通块上亮度最小的城市,一直减直到该城市亮度为0,然后划分为更小的联通块,一直反复即可让所有城市的灯亮度为0。
反过来做就比较简单,先假设所有点都是孤立的,sum = ∑ i = 1 n b i \sum_{i=1}^n bi i=1nbi,将所有点按照bi从大到小排序,建立一根有根树,枚举所有的城市i,遍历与 i 连接的城市 j ,如果 j 遍历过并且与i的树根父亲不同,那么合并 i 与 j,并将 j 的树根父亲赋值给 i 的树根父亲,由于 i 城市与 j 城市是互相联通并且 bi <= bj,在将 j 城市灯亮度变为0之前 i 城市灯亮度已经为0,所以 i 城市亮度计算重复,sum -= bi。(PS:并查集需要使用路径压缩)

AC_CODE:

const int maxn = 4e6+5;

struct node{
	int value;
	int index;	
}k[maxn];

VI G[maxn];
bool vis[maxn];
int fa[maxn];

int get_father(int x) {
	if(x == fa[x]) return x;
	return fa[x] = get_father(fa[x]);
}

void merge(int x, int y) {
	x = get_father(x);
	y = get_father(y);
	if(x != y) {
		fa[x] = y;
	}
}

void solve(){
	int n,m;
	R(n,m);
	ll sum = 0;
	for(int i=1; i<=n; ++i) {
		R(k[i].value);
		k[i].index = i;
		G[i].clear();
		vis[i] = false;
		fa[i] = i;
		sum += k[i].value;
	}
	sort(k+1,k+1+n,[](node x, node y){return x.value > y.value;});
	for(int i=1; i<=m; ++i) {
		int u,v;
		R(u,v);
		G[u].PB(v);
		G[v].PB(u);
	}
	for(int i=1; i<=n; ++i) {
		for(auto x: G[k[i].index]) {
			if(vis[x]) {
				int root = get_father(x);
				if(root != get_father(k[i].index)) {
					merge(x,k[i].index);
					sum -= k[i].value;
				}
			}
		}
		vis[k[i].index] = true;
	}
	W(sum);
}

New Equipments

题意:
有n个工人和m个设备,每个工人只能使用一台设备,一台设备最多只能给一个人使用,每个工人有a,b,c三个属性,工人选择第i台设备,要花费a × i2 + b × i + c的费用,输出n个数,表示i个工人选择i台设备的最少费用。n<=50<=m<=1e8。

题解:
a × i2 + b × i + c是个开口向上的二次函数,并且最多只有50台设备分配给所有工人,所以我们只要考虑二次函数对称轴左右各55个点即可。考虑以下建图方式,建立一个超级源点S和一个超级汇点T,源点与所有工人建立n条容量为1,费用为0的边,所有工人与对称轴左右50个点的机器建立110条容量为1,费用为a × i2 + b × i + c的边,所有与工人连接的机器与汇点建立容量为1,费用为0的边,跑费用流即可。根据最小费用流算法原理和增广路定理,进行i次增广时,产生的费用就是i个工人选择i台设备的最少费用。PS:由于题目中m,b的值都很大,所以需要离散化。

AC_CODE:

const int maxn = 1e5+5;
const ll INF = 1LL<<61LL;
bool vis[maxn];
int n, m, s, t;
int u, v, c, w;
ll cost[maxn];
int pre[maxn], last[maxn], flow[maxn];
ll maxFlow, minCost;
struct Edge
{
	int from, to, flow;
	ll cost;
}edge[maxn];

int head[maxn], num_edge;
int ed[maxn];

queue <int> q;

void addedge(int from, int to, int flow, ll cost)
{
	edge[++num_edge].from = head[from];
	edge[num_edge].to = to;
	edge[num_edge].flow = flow;
	edge[num_edge].cost = cost;
	head[from] = num_edge;

	edge[++num_edge].from = head[to];
	edge[num_edge].to = from;
	edge[num_edge].flow = 0;
	edge[num_edge].cost = -cost;
	head[to] = num_edge;

}

bool SPFA(int s, int t)
{
//	memset(cost, 0x7f, sizeof(cost));
	memset(flow, 0x7f, sizeof(flow));
	for(int i=0; i<maxn; ++i) cost[i] = INF;
	memset(vis, 0, sizeof(vis));
	q.push(s); vis[s] = 1; cost[s] = 0; pre[t] = -1;

	while (!q.empty())
	{
		int now = q.front();
		q.pop();
		vis[now] = 0;
		for (int i = head[now]; i != -1; i = edge[i].from)
		{
			if (edge[i].flow>0 && cost[edge[i].to]>cost[now] + edge[i].cost)
			{
				cost[edge[i].to] = cost[now] + edge[i].cost;
				pre[edge[i].to] = now;
				last[edge[i].to] = i;
				flow[edge[i].to] = min(flow[now], edge[i].flow);
				if (!vis[edge[i].to])
				{
					vis[edge[i].to] = 1;
					q.push(edge[i].to);
				}
			}
		}
	}
	return pre[t] != -1;
}

void MCMF()
{
	maxFlow = 0;
	minCost = 0;
	bool ok = 0;
	while (SPFA(s, t))
	{
		if(ok) printf(" ");
		ok = 1;
		int now = t;
		maxFlow += flow[t];
		minCost += flow[t] * cost[t];
		while (now != s)
		{
			edge[last[now]].flow -= flow[t];
			edge[last[now] ^ 1].flow += flow[t];
			now = pre[now];
		}
		printf("%lld", minCost);
	}
	puts("");
}

ll aa[maxn],bb[maxn],cc[maxn];
struct node2{
	int idx;
	ll value;
}k[maxn];

void solve(){
	memset(head, -1, sizeof(head)); num_edge = -1;//初始化 
	memset(ed, 0, sizeof(ed));
	R(n,m);
	for(int i=1; i<=n; ++i) {
		R(aa[i],bb[i],cc[i]);
	}
	int S = 1e5-2, T = 1e5-1;
	for(int i=1; i<=n; ++i) {
		addedge(S,i,1,0);
	}
	s = S; t = T;
	int idx = n+1;
	map<int,int>mp;
	for(int i=1; i<=n; ++i) {
		ll u = -bb[i]/(2*aa[i]);
		int cnt = 1;
		for(int j=max(1LL,u-55); cnt<=110&&j<=m; cnt++,j++) {
			k[cnt].idx = j;
			k[cnt].value = aa[i] * j * j + bb[i] * j + cc[i];
		}
		sort(k+1,k+cnt,[&](node2 x, node2 y){return x.value < y.value;});
		for(int j=1; j<cnt; ++j) {
			if(!mp[n+k[j].idx]) {
				mp[n+k[j].idx] = idx++;
			}
			addedge(i,mp[n+k[j].idx],1,k[j].value);
		}
	}
	for(auto x: mp) {
		addedge(x.S, T, 1, 0);
	}
	MCMF();
}

1006-The Oculus

题意:
设Fi为斐波那契数列,bi为非0即1的数组,对于任意一个正整数x,都有以下性质:

b1×F1 + b2×F2 ++ bn×Fn = x.
bn = 1.
bi * bi+1 = 0.(即不存在bi=1并且bi+1=1)

现在给出三个数a,b,c,这三个数由b1×F1 + b2×F2 + ⋯ + bn×Fn = x表示,并且满足c=a*b,现在将c某一项bi=1修改为0,询问i是多少。

题解:
思路是计算出a×b-c的值,然后判断与哪一项斐波那契数列的值相等。
由于这题给定的数据范围为1e6,所以我们必须求出0到1e6中所有斐波那契数列的值,但在计算中间会爆long long,所以需要用到哈希,取一个尽可能大的模数,在这道题里面一些比较常用的大质数(如998244353,1e9+7)都不能用,因为得换一个更大的模数,因为使用到大模数,会导致a×b爆long long,所以需要使用快速乘。

AC_CODE:

const ll mod = 5000000002453;
ll qmul(ll a,ll b){ ll r=0; while(b){ if(b&1) r=(r+a)%mod; a=(a+a)%mod; b>>=1; } return r;}
const int maxn = 4e6+5;
ll f[maxn];

/*
struct node{
	
}k[maxn];
*/

void init() {
	f[0] = 1LL;
	f[1] = 1LL;
	f[2] = 2LL;
	for(int i=3; i<=maxn-1; ++i) f[i] = (f[i-1] + f[i-2]) % mod;
}

void solve(){
	int n;
	R(n);
	ll a(0),b(0),c(0);
	for(int i=1; i<=n; ++i) { 
		int k; R(k);
		if(k) a = (a + f[i]) % mod;
	}
	R(n);
	for(int i=1; i<=n; ++i) { 
		int k; R(k);
		if(k) b = (b + f[i]) % mod;
	}
	R(n);
	for(int i=1; i<=n; ++i) { 
		int k; R(k);
		if(k) c = (c + f[i]) % mod;
	}
	ll d = (qmul(a,b) % mod - c + mod) % mod;
//	assert(d > 0);
//	debug(a,b,c,d);
	FOR(i,1,maxn-1) {
		if(d % mod == f[i]) {
			W(i);
			return;
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值