202209-1 如此编码(小模拟)
第一题不需要任何的算法优化,用最直接最普通的思想去做就能满分,因为数据很小:
但要注意提示内容,可以得知关键的部分怎么写:
正确代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
long long m;
cin >> n >> m;
vector<long long> a(n), c(n+1), b(n);
c[0] = 1;
for (int i = 0; i < n; i++)
{
cin >> a[i];
c[i+1] = c[i] * a[i];
}
for (int i = 0; i < n; i++)
{
b[i] = m % c[i+1] - m % c[i];
b[i] /= c[i];
cout << b[i] << " ";
}
return 0;
}
202209-2 何以包邮?(小优化)
这里提示内容很直球,暴力枚举会超时,只有七十分:
看到物品种类-总价格的题目,首先想到背包问题;然而该题中价格与容量是同一变量(预算作为约束条件),而且要求尽可能等于m的总价格而不是尽可能多。
根本的思路是不变的,通过对每个物品的选与不选的判断使得时间复杂度为O(n),只需要改变策略:dp[i][j]的 i 仍然表示考虑前 i 个物品,j 仍然表示当前的容量上限为 j,如果第 i 件物品不导致超载且总重量更大,就把它装入。
最后再从包邮价格 x 的角度考虑,从 x 到可能的价格上界进行遍历,一旦dp[n][j]不为0,即表示有一种方案在考虑前n个物品的前提下,可以达到 j 的容量,由此获得最接近 m 的容量。
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <numeric>
using namespace std;
long long dp[32][300005] = {0};
int item[32];
int main()
{
int n, x;
cin >> n >> x;
for (int i = 0; i < n; i++)
{
cin >> item[i];
}
int up = accumulate(item, item+n, 0);
for (int i = 0; i < n; i++)
{
for (int j = 0; j <= up; j++)
{
dp[i+1][j] = max(dp[i][j], dp[i][j-item[i]] + item[i]);
// j is for capacity
if (dp[i+1][j] > j)
dp[i+1][j] = dp[i][j];
}
}
for (int j = x; j <= up; j++)
{
if (dp[n][j] == j)
{
cout << j;
break;
}
}
return 0;
}
202209-3 防疫大数据(大模拟)
大模拟的要点是听话,即,此处不需要任何的算法优化和程序设计的“巧思”,甚至不需要理解。按照提示中的形式化定义去写判断函数,按照题目对问题的描述去分割区间并设置算法,敲进去就对了,否则永远会报错,等debug完了早就考完了。
原本的思路:
class User{
public:
int id;
short last;
int been;
User(int u, int date, int place) : id(u), last(date), been(place){};
bool operator>(const User &other) const{
return last > other.last;
}
};
一开始想要模仿现实中的软件动态管理数据,以优先队列存储一个个用户,根据活跃时间来删除和判断风险,提高查找效率,实际上根本没法做的,也没必要进行优化。
最终的正确做法是面向过程的,以每一次输入操作为中心的,只要老老实实跟着题目复现就完事了:
#include <bits/stdc++.h>
using namespace std;
struct msg{
int date, user, place;
};
vector<msg> Data[1001];
map<int, pair<short, short>> dura;
map<int, bool> sites;
void setDanger(int place, int date)
{
if (!sites[place])
dura[place] = {date, date+6};
else{
if (date <= dura[place].second +1)
dura[place].second = date+6;
else
dura[place] = {date, date+6};
}
sites[place] = true;
}
// 按照原文去复现
bool check(int d1, int u, int p, int d)
{// 用户u在d1天去过p地点,今天(截止日期)是d
if (sites[p] && d-7 < d1 && d1 <= d && d1 >= dura[p].first && d <= dura[p].second)
return true;
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int days;
cin >> days;
for (int today = 0; today < days; today++)
{
int ri, mi;
cin >> ri >> mi;
for (int r = 0; r < ri; r++)
{
int place;
cin >> place;
setDanger(place, today);
}
for (int m = 0; m < mi; m++)
{
int d, u, p;
cin >> d >> u >> p;
Data[today].push_back({d,u,p});
}
vector<int> ans;
// 存在一个d0 in (d-7, d],在d0天收到了某一条信息
for (int d0 = max(today-6, 0); d0 <= today; d0++)
{
for(auto &it : Data[d0])
{
if (check(it.date, it.user, it.place, today))
ans.push_back(it.user);
}
}
sort(ans.begin(), ans.end());
ans.erase(unique(ans.begin(), ans.end()), ans.end());
cout << today;
for (auto &it : ans)
cout << " " << it;
cout << endl;
}
return 0;
}
202209-4 吉祥物投票(大优化)
难点:最多10^9长度的初始为0序列,需要高效的区间访问和更新。
自创的用map优化区间操作的数据结构,先把投票者视作数轴绑定区间赋值关系,再把吉祥物绑定所有与它相关的投票者区间,同时维护一个表存储当前得分榜。
#include <bits/stdc++.h>
using namespace std;
map<int, pair<int, int>> auds; // <num, <x, anotherSide>>
map<int, map<int, int>> coms; // <start, end>
map<int, int> board;
int n, m, q;
void op1(int l, int r, int x){
for (int i = l ; i <= r;)
{
int jump = false;
if (auds[i].first)
{
if (auds[i].second < i){
int L = auds[i].second, y = auds[i].first;
// 探测到右端点,向左让出
auds[L]={y, l-1};
auds[l-1] = {y, L};
auds.erase(i);
// 更新商品的查询表
coms[y][L] = l-1;
coms[y][l-1] = L;
coms[y].erase(i);
board[y] -= (i-l+1);
}else if (auds[i].second > i){
// 除非在l点开始,否则只能探测到左端点
int R = auds[i].second, y = auds[i].first;
if (R <= r){
// 全部覆盖
auds.erase(R);
auds.erase(i);
// 更新查询表
coms[y].erase(R);
coms[y].erase(i);
board[y] -= R - i + 1;
jump = R;
}
else{
// 向右让出
auds[r+1] = {y, R};
auds[R] = {y, r+1};
auds.erase(i);
// 更新查询表
coms[y][R] = r+1;
coms[y][r+1] = R;
coms[y].erase(i);
board[y] -= r - i + 1;
jump = r;
}
}else{
// 单点删除
int y = auds[i].first;
auds.erase(i);
coms[y].erase(i);
board[y]--;
}
}else
{
// 未覆盖
auds.erase(i);
}
if (jump)
i = jump;
else
i++;
}
// 写入该区间
auds[l] = {x, r};
auds[r] = {x, l};
coms[x][l] = r;
coms[x][r] = l;
board[x] += r - l + 1;
}
void op2(int x, int w){
if (w)
board[w] += board[x];
board.erase(x);
auto seq = coms[x];
for (auto &it : seq)
{
int l = it.first, r = it.second;
if (w)
{
auds[l].first = w;
auds[r].first = w;
coms[w][l] = r;
coms[w][r] = l;
}else{
auds.erase(l);
auds.erase(r);
}
}
coms.erase(x);
}
void op3(int x, int y){
auto seqx = coms[x], seqy = coms[y];
for (auto &ity : seqy){
int l = ity.first, r = ity.second;
auds[l].first = auds[r].first = y;
}
for (auto &itx : seqx)
{
int l = itx.first, r = itx.second;
auds[l].first = auds[r].first = x;
}
coms[y] = seqx;
coms[x] = seqy;
int cachex = board[x];
board[x] = board[y];
board[y] = cachex;
}
int op4(int w){
if (w){
return board[w];
}
else{
int ans = 0;
for (auto &every : coms)
{
int name = every.first;
ans += board[name];
}
return n - ans;
}
}
int op5(){
int maxVal = 0, name = -1;
for (auto &itb : board)
{
int nm = itb.first, v = itb.second;
if (v > maxVal)
{
maxVal = v;
name = nm;
}
}
if (maxVal == 0)
return 0;
return name;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> q;
vector<int> tt;
for (int i = 0; i < q; i++)
{
short op;
cin >> op;
switch (op)
{
int x, y, w;
case 1:
int l, r;
cin >> l >> r >> x;
op1(l,r, x);
break;
case 2:
cin >> x >> w;
op2(x, w);
break;
case 3:
cin >> x >> y;
op3(x, y);
break;
case 4:
cin >> w;
cout << op4(w) << endl;
// tt.push_back(op4(w));
break;
case 5:
cout << op5() << endl;
// tt.push_back(op5());
break;
}
}
// cout << endl;
// for (auto it:tt)
// {
// cout << it << endl;
// }
return 0;
}
通过了测试用例,然而由于未知原因提交永远错误,且超时。
满分通过的代码,运用了珂朵莉树,如果用数组模拟仅得20分:
#include<bits/stdc++.h>
using namespace std;
struct node // Chtholly tree
{
int l,r;
mutable int v;//区间值
node(int l,int r,int v):l(l),r(r),v(v){}
bool operator <(const node &a) const {return l<a.l;}//重载<比较符,使区间在set中为从小到大的顺序排列
};
struct Node//维护区间最值
{
int k,v;//k编号,v投票数
bool operator <(const Node& a) const
{
if(v!=a.v)return v>a.v;
return k<a.k;
}
};
using SET=set<node>::iterator;
set<node> t; // Chthollty tree
int id[200005],ans[200005],f[200005];//懒标记,投票数数组,并查集
map<int,int> inv; // 建立标记和标签之间双射
set<Node> all;//维护区间最值
int n,m,q,num;
int find(int k)
{
if(k==f[k])return k;
else return f[k]=find(f[k]);
}
SET split(int pos)
{
SET it=t.lower_bound(node(pos,0,0));
if(it!=t.end()&&it->l==pos)return it;
--it;
int l=it->l,r=it->r,v=it->v;
t.erase(it);
t.insert(node(l,pos-1,v));
return t.insert(node(pos,r,v)).first;
}
void assign(int l,int r,int v)
{
SET itr=split(r+1),itl=split(l);
int father;
for(SET it=itl;it!=itr;it++)
{
father=find(it->v);
all.erase({father,ans[father]});
ans[father]-=(it->r-it->l+1);
all.insert({father,ans[father]});
}
t.erase(itl,itr);
father=find(v);
t.insert(node(l,r,father));
all.erase({father,ans[father]});
ans[father]+=(r-l+1);
all.insert({father,ans[father]});
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>q;
num=m;ans[0]=n;
for(int i=0;i<=2*max(m,q);i++)id[i]=f[i]=i;
for(int i=1;i<=m;i++)inv[i]=i;
t.insert(node(1,n,0));all.insert({0,n});
for(int i=1;i<=q;i++)
{
int x,l,r,v,w;
cin>>x;
if(x==1)
{
cin>>l>>r>>v;
assign(l,r,id[v]);
}
if(x==2)
{
cin>>v>>w;
int fx=id[v],fy=id[w];
all.erase({fx,ans[fx]});all.erase({fy,ans[fy]});
ans[fy]+=ans[fx];
all.insert({fy,ans[fy]});
int X=find(fx),Y=find(fy);
f[X]=Y;//x的父亲为y的父亲
id[v]=++num;inv[num]=v;//新建一个v编号的标记,以便重新出现时使用
}
if(x==3)
{
cin>>v>>w;//注意inv交换
swap(inv[id[v]],inv[id[w]]);swap(id[v],id[w]);
}
if(x==4)
{
cin>>v;
cout<<ans[id[v]]<<endl;
}
if(x==5)
{
int maxx=0,idans=0;
for(auto it:all)
{
if(it.k&&it.v>maxx){maxx=it.v;idans=inv[it.k];}
if(it.k&&it.v==maxx&&inv[it.k]<idans)idans=inv[it.k];//找到相同编号更小的
if(maxx>it.v)break;
}
cout<<idans<<endl;
}
}
return 0;
}
总结:小模拟小优化争取二三十分钟内解决,大模拟老老实实去复现题干的语言,剩下时间专攻大优化尽量得分,第五题图一乐就完了。一以贯之的是一定要准确读题,尤其是提示。