2022年“图森未来杯”全国程序设计邀请赛(校外组) - VOJ
E 无独有偶(Easy)(dfs)
题目:对于 n 名同学,每个人都有一个对应的实力值 ai(实力值可能相同),组成序列 a1,⋯,an。本质不同的实力值子序列是指长度不同或长度相同但对应位置上数不同的实力值子序列。现在你需要回答有多少至少出现 两次 的本质不同实力值子序列。
思路:dfs,枚举当前数字 装和不装的子序列,并用map记录,最后统计至少出现两次的个数
虽然不知道为什么O2没有超时
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;int a[30];int t;
map<vector<int>,int>mp;
void dfs(vector<int> aa,int i){
if(i>n)return;
dfs(aa,i+1);
aa.push_back(a[i]);
mp[aa]++;
dfs(aa,i+1);
}
signed main()
{
cin>>n;int c=0; vector<int>xx;
for(int i=1;i<=n;i++)cin>>a[i];
dfs(xx,1);
for(auto x:mp) if(x.second>=2)c++;
cout<<c<<'\n';
}
J 中庸之道(对顶堆)
题目:现在你希望能设计一个数据结构,来实现如下两种操作,
1 x
,表示加入在数组最后添加一个数 x。2
,表示这个数组的中位数。
注意,最开始的时候数组是空的。中位数的定义为将所有数从小到大排序后的序列 A 中,下标为 ⌈∣A∣/2⌉ 的数的值。
注意:比赛的时候知道是对顶堆,但是忘记输入的时候要比较堆顶元素了,反省反省……
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
priority_queue<int, vector<int>, less<int> > qmax;
priority_queue<int, vector<int>, greater<int> >qmin;
int n;cin>>n;
for(int i=0;i<n;i++){
int x;cin>>x;
if(x==1){
int op;cin>>op;
if (qmax.empty()||op<=qmax.top()) qmax.push(op);
else qmin.push(op);
}
if(x==2){
while(qmax.size()>(qmax.size()+qmin.size()+1)/2){
qmin.push(qmax.top());
qmax.pop();
}
while(qmax.size()<(qmax.size()+qmin.size()+1)/2){
qmax.push(qmin.top());
qmin.pop();
}
cout<<qmax.top()<<'\n';
}
}
return 0;
}
F 木桶效应(单调栈)
题目:假设 有 n 块木板,这 n 块木板排成一排,每块木板长度为 ai。当使用连续的一段木板作木桶时,木桶的容量为这些木板长度的最小值。对于任意一个 x(1≤x≤n),你需要回答如果使用 n 块木板中的连续 x 块木板做木桶,木桶的最大容量分别为多少?
思路:
1. 求特定区间长度的最小值,首先先遍历每一个点,求出当前这个点右边第一个比它小的数字(用单调栈求),再求出当前这个点左边第一个比它小的数字(用单调栈求)。
2. 枚举每一个点,区间(l[i],r[i])的最小值就是a[i],所以 c[r[i]-l[i]-1] = max(a[i], c[r[i] - l[i] - 1]);
3.因为有的区间数字可能没有求到,例如下面的样例,只按上面的操作会得到 9 7 0 0 4 0 3 0 2 1
10
9 2 7 3 5 6 4 8 7 1
已知 小区间(区间长度为x)的值大于等于大区间的值,如果小区间的值为0,说明大区间去掉左右的某个数,最小值依然没变,所以小区间x的值等于大区间x+1的值
代码
#include <bits/stdc++.h>
#define N 1000010
#define MOD 1000000007
#define LL long long
using namespace std;
int n, r[N], l[N], a[N], c[N], ans[N];
stack<int> s;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; ++i) {
r[i] = n + 1;
}
for (int i = 1; i <= n;) {
while (!s.empty() && a[i] < a[s.top()]) {
r[s.top()] = i;//右边的第一小的数字
s.pop();
}
s.push(i++);
}
while (!s.empty()) s.pop();
for (int i = n; i >= 1;) {
while (!s.empty() && a[i] < a[s.top()]) {
cout<<s.top()<<" "<<i<<endl;
l[s.top()] = i;//左边的第一小的数字
s.pop();
}
s.push(i--);
}
for (int i = 1; i <= n; ++i) {
c[r[i]-l[i]-1]= max(a[i],c[r[i]-l[i]-1]);//(l[i],r[i])区间的最小值是a[i]
}
for (int i = n;i>=1;--i) {
if (c[i] < c[i + 1])c[i] = c[i + 1];//如果没有这个,有些区间会没有,输出为0
}
for (int i = 1; i <= n; ++i) {
printf("%d ", c[i]);
}
return 0;
}
G 正多边形(几何)(思维)
题目:给定二维格点上的n的点(x1,y1),(x2,y2),…,(xn,yn),是否存在m个点能构成正m边形?
输入描述:第一行包括两个整数n,m分别表示点数和正多边形的边数。接下来n行,每行 两个整数 表示 (xi,yi)。
提示:
思路:点是整数 ,在纸上自己试试,m=3的时候是做不到正三角形的!!!m=4可以,m=5不可以……所以直接暴力枚举做,范围1e2,n的四次方时间复杂度也不会超时
(所以提示里的求重心什么的其实不用看……)
代码
#include <bits/stdc++.h>
using namespace std;
long long sqrdist(pair<long long, long long> p1, pair<long, long> p2) {
return (p1.first-p2.first)*(p1.first-p2.first)+(p1.second-p2.second)*(p1.second- p2.second);
}
int main() {
int n, m;
vector<pair<long long, long long> > points;
cin >> n >> m;
for (int i = 0; i < n; i++) {
pair<long long, long long> tmp;
cin >> tmp.first >> tmp.second;
points.push_back(tmp);
}
if (m != 4) {cout << "No" << endl;return 0;}
else {
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
for (int k = 0; k < n; k++)
for (int l = 0; l < n; l++) {
if (i == j || i == k || i == l || j == k || j == l || k == l)
continue;
if (sqrdist(points[i], points[j]) != sqrdist(points[j], points[k]))
continue;
if (sqrdist(points[j], points[k]) != sqrdist(points[k], points[l]))
continue;
if (sqrdist(points[k], points[l]) != sqrdist(points[l], points[i]))
continue;
if (sqrdist(points[i], points[j]) + sqrdist(points[j], points[k]) !=
sqrdist(points[i], points[k]))
continue;
if (sqrdist(points[j], points[k]) + sqrdist(points[k], points[l]) !=
sqrdist(points[j], points[l]))
continue;
if (sqrdist(points[k], points[l]) + sqrdist(points[l], points[i]) !=
sqrdist(points[k], points[i]))
continue;
cout << "Yes" << endl;
return 0;
}
cout << "No" << endl;
}
}
H 一起去旅行
题目: 现在你有一辆电驴最多只能存储 t 的电量,给出一张 n 个点 m 条边的无向图,表示旅行地图,你需要从 1 号点去往 n 号点。每条边有一个权值表示经过这条边所需要耗费的电量。
每个点上都有一个充电桩,如果你在此处充 1 单位的电量需要耗费 wi 元。当然,我们不允许你在途中抛锚,即你必须保证每到达一个点,其小电驴的电量均大于等于 0。
请问你到达 n 号点至少需要充多少电费。初始时你的小电驴电量为 0。
思路:分层图
理解分层图之前,先看一下分层图的模板题 P4568 [JLOI2011] 飞行路线
对于能修改k次路径,于是原先图是第0层,向下可以有k层图,简而言之就是类似于暴力地把每个可能修改为0的路径全部陈列出来,层与层之间的路是单向的,同一层之间的路就和原图一样,之后再经过dijkstra算最短路就可以了。
P4568 [JLOI2011] 飞行路线的代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
vector<pair<int,int>>mp[1000004];
typedef pair<long long,int> pll;
int dis[1000004];
bool vis[1000004];
void dijkstra(int u)//
{
memset(dis, 0x3f, sizeof dis);
memset(vis,0,sizeof vis);
dis[u] = 0;
priority_queue<pll, vector<pll>,greater<pll> >heap;
heap.push({0,u});
while (heap.size()){
pll t = heap.top();heap.pop();
int ver = t.second;
if (vis[ver]) continue;
vis[ver]=true;
for(auto k:mp[ver]){
if(!vis[k.second]&&dis[k.second]>dis[ver]+k.first){
dis[k.second]=dis[ver]+k.first;
heap.push({dis[k.second],k.second});
}
}
}
}
signed main()
{
int n,m,k;cin>>n>>m>>k;
int st,be;cin>>st>>be;
for(int i=1;i<=m;i++){
int a,b,c;cin>>a>>b>>c;
mp[a].push_back({c,b});
mp[b].push_back({c,a});
for(int i=0;i<k;i++){
mp[a+i*n].push_back({0,b+(i+1)*n});
mp[b+i*n].push_back({0,a+(i+1)*n});
mp[a+(i+1)*n].push_back({c,b+(i+1)*n});
mp[b+(i+1)*n].push_back({c,a+(i+1)*n});
}
}
dijkstra(st);
int ans=0x3f3f3f3f;
for(int i=0;i<=k;i++)
ans=min(dis[be+i*n],ans);
cout<<ans<<endl;
return 0;
}
现在理解了分层图的板子,再来看看H题
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define _10(x) (int)1e##x
typedef long long LL;
#define N (3 * _10(6))
int n, m, t, w[N];
LL d[N];
vector<pair<int, int> > v[N];
priority_queue<pair<int, LL> > q;
int getnode(int x, int lft) {
return (x-1)*(t+1)+lft;//t是每个点的层数,lft是下一个点
}
void link(int x, int y, int c) {
v[x].push_back(make_pair(y, c));
}
int main() {
cin>>n>>m>>t;
for(int i=1;i<=n;i++)cin>>w[i];
for(int i=1;i<=n;i++)
for(int j=0;j<t;j++){
link(getnode(i,j),getnode(i,j+1),w[i]);//所有点,每一层是当前节点充了多少度电
}
for(int i=1;i<=m;i++) {
int x,y,c;int xx,yy;cin>>x>>y>>c;
for(int j=c;j<=t;j++){
link(getnode(x,j),getnode(y,j-c),0);//在x充了j度电,可以走到y点还剩下j-c度电
link(getnode(y,j),getnode(x,j-c),0);
}
}
for(int i=0;i<=getnode(n,t);i++)d[i]=(LL)1e16;
d[0]=0;
q.push(make_pair(0,0));
while (!q.empty()) {
auto tmp = q.top(); q.pop();
for(auto nd : v[tmp.se]) {
if (d[nd.fi] > d[tmp.se] + nd.se) {
d[nd.fi] = d[tmp.se] + nd.se;
q.push(make_pair(-d[nd.fi], nd.fi));
}
}
}
LL ans = (LL)1e16;
for(int i = 0; i <= t; i++) ans = min(ans, d[getnode(n, i)]);
cout << ans << endl;
}