The 13th Shandong ICPC Provincial Collegiate Programming Contest(VP)

文章涉及编程题目,包括使用AI排序、工种分配策略、贪心方法求解、位运算技巧以及计算几何中的多边形问题,展示了在IT技术场景下如何通过算法解决实际问题。
摘要由CSDN通过智能技术生成

A. Orders

按天数ai排序,检查存货量即可

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;

#define ll long long

struct QWQ
{
	ll x,y;
}a[N];

bool cmp(QWQ A,QWQ B)
{
	return A.x < B.x;
}

void Solve()
{
	ll n,m;
	cin >> n >> m;
	for(int i = 1; i <= n; i ++ )
	{
		cin >> a[i].x >> a[i].y;
	}
	sort(a + 1, a + n + 1,cmp);
	ll sum = 0;
	for(int i = 1; i <= n; i ++ )
	{
		sum += a[i].y;
		if(sum > a[i].x * m)
		{
			cout << "No" << '\n';
			return;
		}
	}
	cout << "Yes" << '\n';
}

int main()
{
	int t = 1;
	cin >> t;
	for(int i = 1; i <= t; i ++ ) Solve();
	return 0;
}

B. Building Company

这道题目有点像拓扑,我们先将不同工种所对应的项目和要求人数分别存起来,再将每个项目可以吸引的不同工种和人数也存起来。

我们从已有的工种去判断满足哪些工程对这个工种的需求,如果满足则对这个工程进行+1的标记,直到这个工程所有的工种限制全部满足时,再将该项目吸引的工种进行更新,并重新判断该工种所对应的工程。

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;

#define ll long long
#define edl '\n'

map<ll,set<pair<ll,ll>>>mp;
map<ll,ll>now;
ll goal[N],res[N];
vector<pair<ll,ll>> add[N];
void Solve()
{
	ll n,m;
	cin >> n;
	for(int i = 1; i <= n; i ++ )
	{
		ll x,y;
		cin >> x >> y;
		now[x] += y;
	}
	cin >> m;
	ll ans = 0;
	ll s;
	for(int i = 1; i <= m; i ++ )
	{
		cin >> s;
		goal[i] = s;
		for(int j = 1; j <= s; j ++ )
		{
			ll x,y;
			cin >> x >> y;
			mp[x].insert({y,i});
		}
		cin >> s;
		if(!goal[i]) ans ++;
		for(int j = 1; j <= s; j ++ )
		{
			ll x,y;
			cin >> x >> y;
			if(!goal[i]) now[x] += y;
			else add[i].push_back({x,y});
		}
	}
	vector<ll>check;
	for(auto &it : mp)
	{
		vector<pair<ll,ll>>re;
		for(auto [x,y] : it.second)
		{
			if(x <= now[it.first])
			{
				re.push_back({x,y});
				res[y] ++;
				if(res[y] == goal[y])
				{
					ans ++;
					for(auto [xx,yy] : add[y])
					{
						if(xx < it.first) check.push_back(xx);
						now[xx] += yy;
					}
				}
			}else break;
		}
		for(auto itt : re)
		{
			it.second.erase(itt);
		}
	}
	while(check.size())
	{
		vector<ll>tmp;
		for(auto it : check)
		{
			vector<pair<ll,ll>>re;
			for(auto [x,y] : mp[it])
			{
				if(x<=now[it])
				{
					re.push_back({x,y});
					res[y] ++;
					if(res[y]==goal[y])
					{
						ans++;
						for(auto [xx,yy]:add[y])
						{
							tmp.push_back(xx);
							now[xx]+=yy;
						}
					}
				}else break;
			}
			for(auto itt:re){
				mp[it].erase(itt);
			}
		}
		check = tmp;
	}
	cout << ans << edl;
}

int main()
{
	int t = 1;
	// cin >> t;
	for(int i = 1; i <= t; i ++ ) Solve();
	return 0;
}

D. Fast and Fat

这道题,首先你能快速想到二分答案然后判断是否成立。

假设答案为x 那么,之有v1 + w1 - w2 >= x 时1才可以帮助2

可以将式子转化为v1 + w1 - x >= w2

那么怎么判断呢,赛时的时候我用了最暴力的lowerbound,去查找第一个大于等于需要帮助的人,这样是最贪心的。复杂度是nloglog

赛后突然发现,其实完全不用这么麻烦,只需要先按v1 + w1排序,再按w1排序能力最强的帮助困难最大的即可,复杂度nlog

struct QWQ
{
	ll v,w;
}a[N],b[N];

bool cmp(QWQ A,QWQ B)
{
	return A.v + A.w < B.v + B.w;
}

bool cmp1(QWQ A,QWQ B)
{
	return A.w < B.w;
}

int ck(ll x)
{
	vector<ll>g(n,0);
	ll l = 0;
	ll r = 0;
	for(int i = n; i >= 1; i -- )
	{
		if(a[i].v >= x)
		{
			g[l++] = (a[i].v + a[i].w - x);
		}
	}
	for(int i = n; i >= 1; i -- )
	{
		if(b[i].v < x)
		{
			if(g[r++] < b[i].w) return 0;
		}
	}
	return 1;
}

void Solve()
{
	cin >> n;
	for(int i = 1; i <= n; i ++ )
	{
		cin >> a[i].v >> a[i].w;
		b[i] = a[i];
	}
	sort(a + 1, a + n + 1,cmp);
	sort(b + 1, b + n + 1,cmp1);
	ll l = 0;
	ll r = 10000000000;
	while(l <= r)
	{
		ll mid = (l + r) >> 1;
		if(ck(mid)) l = mid + 1;
		else r = mid - 1;
	}
	cout << r << edl;
}

E. Math Problem

这题一定是先除法n次再乘法m次,绝不可能乘完再除这样是无意义的。

所以只需要枚举除法次数再枚举乘法次数即可。

ll get(ll n, ll m, ll a)
{
    ll inv = 1;
    ll now = 0;
    for(ll x = n % m; 1; x = (x * k) % m, inv *= k)
    {
        if(x + inv > m || x == 0)
        {
            return now;
        }
        now += a;
    }
    return -1;
}
void Solve()
{
    cin >> n >> k >> m >> a >> b;
    if(n % m == 0)
    {
        cout << "0" << edl;
        return;
    }
    if(k == 1)
    {
        cout << "-1" << edl;
        return;
    }
    ll ans = 3e18;
    ll now = 0;
    for(ll x = n; x % m != 0; x /= k)
    {
        ans = min(ans, get(x, m, a) + now);
        now += b;
    }
    ans = min(ans, now);
    cout << ans << edl;
}

G. Matching

i - j == ai - aj转换为ai - i == aj - j

所以我们可以将其分成不同的集合,集合内贪心即可

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;

#define ll long long
#define edl '\n'

ll a[N];
map<ll,set<pair<ll,ll>>>mp;
void Solve()
{
	mp.clear();
	ll n;
	cin >> n;
	for(int i = 1; i <= n; i ++ )
	{
		cin >> a[i];
		mp[a[i] - i].insert({a[i],i});
	}
	ll ans = 0;
	for(auto it : mp)
	{
		auto g = it.second;
		pair<ll,ll>A;
		for(auto ok : g)
		{
			A = ok;
			break;
		}
		if(g.size() % 2) g.erase(A);
		ll sum = 0;
		ll ok1 = 0;
		for(auto ok : g)
		{
			if(ok1)
			{
				ok1 = 0;
				sum += ok.first;
				if(sum >= 0) ans += sum;
				sum = 0;
			}
			else
			{
				ok1 = 1;
				sum += ok.first;
			}
		}
	}
	cout << ans << edl;
}

int main()
{
	int t = 1;
	cin >> t;
	for(int i = 1; i <= t; i ++ ) Solve();
	return 0;
}

I Three Dice

枚举所有点数即可ac

J. Not Another Path Query Problem

这是一道位运算的题目,我们很容易想到按位去枚举答案。

一条路径的边权>=V可以分成两种情况:

1.等于也就是V二进制中为1的,路径边权值上也一定要为1

2.大于,V的第i位为0时,边权这一位为1,前面与V相同,后面任意

然后我们可以通过并查集来判断了

int ans[N];
struct node
{
    int u,v;
    ll w;
}e[N];

int fa[N];
int getf(int x)
{
    if(fa[x] == x) return x;
    return fa[x] = getf(fa[x]);
}
void mergee(int u,int v)
{
    u = getf(u); v = getf(v);
    if(u != v) fa[u] = v;
}

int qu[N],qv[N];

void Solve(){
    int n,m,Q;
    ll V;
    cin >> n >> m >> Q >> V;
    for(int i = 1;i <= m;i ++) cin >> e[i].u >> e[i].v >> e[i].w;
    for(int i = 1;i <= Q;i ++) cin >> qu[i] >> qv[i];
    ll pre = 0;
    for(int i = 60;i >= -1;i --)
    {
        if(i >= 0 && ((V >> i) & 1)) pre |= (1ll << i);
        else
        {
            ll cur = pre | (1ll << i);
            if(i == -1) cur = V;
            for(int j = 1;j <= n;j ++) fa[j] = j;
            for(int j = 1;j <= m;j ++)
            {
                if((cur & e[j].w) == cur) mergee(e[j].u,e[j].v);
            }
            for(int j = 1;j <= Q;j ++) if(getf(qu[j]) == getf(qv[j])) ans[j] = 1;
        }
    }
    for(int i = 1;i <= Q;i ++) cout << (ans[i] ? "Yes" : "No") << edl;
}

L. Puzzle: Sashigane

你先想一想一个边长为n的正方形是不是一定能同过m个L变成边长为n+m的正方形。

这道题就考了这个,剩下的交给dfs。。。

struct R
{
	ll a, b, c, d;
};
vector<R>g;
void dfs (int x, int y, int x2, int y2)
{
	if (x == x2 && y == y2) return;
	if (x != u && y != v)
	{
		g.push_back({x, y, x2 - x, x2 - x});
		dfs (x + 1, y + 1, x2, y2);
	}
	else if (x != u && y2 != v)
	{
		g.push_back({x, y2, x2 - x, x - x2});
		dfs (x + 1, y, x2, y2 - 1);
	}
	else if (x2 != u && y != v)
	{
		g.push_back({x2, y, x - x2, x2 - x});
		dfs (x, y + 1, x2 - 1, y2);
	}
	else if (x2 != u && y2 != v)
	{
		g.push_back({x2, y2, x - x2, x - x2});
		dfs (x, y, x2 - 1, y2 - 1);
	}
 
}
void solve()
{
	cin >> n >> u >> v;
	dfs (1, 1, n, n);
	cout << "Yes" << edl;
	cout << g.size() << edl;
	for (auto [a, b, c, d] : g)
	{
		cout << a << " " << b << " " << c << " " << d << edl;
	}
}

M. Computational Geometry

考虑固定边 bc 后,多边形可以分为两部分,第一部分为 bc 所在的 (k + 1) 边形,第二部分则为 abc 构成的三角形。

第一部分面积可以用鞋带公式来做,第二部分可以用指针来维护,这是凸多边形所以当bc逆时针动时,最优的a点也一定逆时针动或不动。

struct QWQ
{
	ll x,y;
}a[N];

long long sum3(QWQ A,QWQ B,QWQ C)
{
	ll ok = 0;
	ok = A.x * B.y + B.x * C.y + C.x * A.y - A.y * B.x - B.y * C.x - C.y * A.x;
	return ok;
}

void Solve()
{
	memset(a,0,sizeof(a));
	cin >> n >> m;
	for(int i = 1; i <= n; i ++ )
	{
		cin >> a[i].x >> a[i].y;
	}
	ll l = 1;
	ll r = 1 + m;
	long double sum = 0;
	for(int i = l; i < r; i ++ )
	{
		sum += a[i].x * a[i + 1].y;
		sum -= a[i].y * a[i + 1].x;
	}
	sum += a[r].x * a[1].y;
	sum -= a[r].y * a[1].x;
	long double ans = 0;
	ll st;
	for(st = r + 1; ; st ++ )
	{
		st %= mod;
		if(!st) st = n;
		ll op = st + 1;
		op %= n;
		if(!op) op = n;
		if(sum3(a[l],a[r],a[op]) > sum3(a[l],a[r],a[st])) continue;
		else break;
	}
	ans = max(ans,sum3(a[l],a[r],a[st]) + sum);
	for(int i = 1; i <= n; i ++ )
	{
		ll lxt = l + 1;
		lxt %= n;
		if(!lxt) lxt = n;
		ll rxt = r + 1;
		rxt %= n;
		if(!rxt) rxt = n;
		sum -= a[r].x * a[l].y;
		sum -= a[l].x * a[lxt].y;
		sum += a[r].x * a[rxt].y;
		sum += a[rxt].x * a[lxt].y;
		sum += a[r].y * a[l].x;
		sum += a[l].y * a[lxt].x;
		sum -= a[r].y * a[rxt].x;
		sum -= a[rxt].y * a[lxt].x;
		l ++;
		r ++;
		l %= n;
		r %= n;
		if(!l) l = n;
		if(!r) r = n;
		ll op = st + 1;
		op %= n;
		if(op == 0) op = n;
		while(sum3(a[l],a[r],a[st]) < sum3(a[l],a[r],a[op]))
		{
			st = op;
			op ++;
			op %= n;
			if(op == 0) op = n;
		}
		ans = max(ans,sum3(a[l],a[r],a[st]) + sum);
	}
	cout << SPO(12) << ans / 2.0 << edl;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值