题目链接

D题题意出锅了,在此再次向各位选手表示道歉。

写在前面的废话
出题容易验题难,写题解更难,鄙人第一次写详细题解,表达能力可能有所欠缺,有些知识点附上了他人的文章链接,希望有助于理解www。
BCD题题解分别由wph,zzl,syd编写,KLM题题解由gjp编写,其余的7道题为鄙人编写,在此直接贴过来了他们写的题解。
难度

本人认为难度排序:A<D<C<B<E<F<H=I<J=G<K=L=M
实际上从过题人数来看中间和后面几道题的难度确实差别不大,过的人数相差不大,但B和D过的人数比我预期要少,可能也是受到题意的问题影响。

文章目录

  • A.提瓦特的春节
    • 代码
  • B.考试周
    • 代码
  • C.No Acyclic
    • 代码
  • D.国王的游戏
    • 代码
  • E.学以致用
    • 代码
  • F.平稳变化
    • 代码
  • G.拜登打牌
    • 代码
  • H. An Easy Problem
    • 代码
  • I.求求你啦,帮帮可莉吧
    • 代码
  • J.嘉然小姐的魔法通道
    • 代码
  • K.嘉然小姐的魔法日记
    • 代码
  • L.七海の新春旅行
    • 代码
  • M.嘉然小姐的新春礼物
    • 代码

A.提瓦特的春节

签到题,输出16即可,提前祝大家春节快乐。

代码

#include<bits/stdc++.h>
#define Happy int main(){#define Spring std::cout<<16;
#define Festival return 0;}Happy Spring Festival

B.考试周

考察算法:
基础动态规划
思路:
建立一个 fff 数组,f[i]f[i]f[i]表示当第 iii 位同学传递物资,前 i−1i - 1i−1 位同学最少付出的代价。容易得到状态转移方程
f[i]=minf[k](i−m≤k≤i−1)+w[i]f[i]=min{f[k]}(i-m \leq k \leq i-1)+w[i]f[i]=minf[k](i−m≤k≤i−1)+w[i]
Case 1:
暴力地对每个 iii 扫前面的 mmm 个同学(如果前面有足够的人),时间复杂度O(nm)O(nm)O(nm)
Case 2:
单调队列优化,时间复杂度O(n)O(n)O(n)
下面给出暴力代码,感兴趣的选手如果还不会的话可以学习一下单调队列优化。

代码

#include <iostream>
#include <cstdio>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 200 + 10;
int n, m, t, k;
int a[N], f[N];
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin >> n >> m;for (int i = 1;i <= n;i++) {cin >> a[i];if (i <= m) f[i] = a[i];}for (int i = m + 1;i <= n;i++) {int res = inf;for (int j = max(i - m, 0);j < i;j++) {res = min(res, f[j]);}f[i] = res + a[i];}int ans = inf;for (int i = n - m + 1;i <= n;i++) {ans = min(ans, f[i]);}cout << ans << endl;return 0;
}

C.No Acyclic

考察算法:
简单模拟
思路:
只需从头到尾遍历该字符串,并建立一个 visvisvis 数组,将需要删除的标记即可。
时间复杂度:O(n)O(n)O(n)

代码

#include <bits/stdc++.h>
using namespace std;
bool vis[1010];
int main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);string s;cin >> s;int n = s.length();for(int i = 0; i <= n - 6; i++) {if((s[i]=='a'||s[i]=='A')&&(s[i+1]=='c'||s[i+1]=='C')&&(s[i+2]=='y'||s[i+2]=='Y')&&(s[i+3]=='c'||s[i+3]=='C')&&(s[i+4]=='l'||s[i+4]=='L')&&(s[i+5]=='i'||s[i+5]=='I')&&(s[i+6]=='c'||s[i+6]=='C')) {for(int j = 0; j <= 6; j++)vis[i+j] = 1;}}for(int i = 0; i < n; i++) {if(!vis[i]) cout << s[i];}cout << '\n';return 0;
}

D.国王的游戏

考察算法:
贪心,乘法逆元
思路:
易知若要得到奖赏最大,需要右手数字最小的大臣站在队伍最后。

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int mod = 100003;
ll fastPower(ll base, ll power) {ll result = 1;while (power > 0) {if (power & 1) { result = result * base % mod;}power >>= 1;base = (base * base) % mod;}return result;
}
void solve()
{int n;cin >> n;ll a, b;ll bmin=1000007;cin >> a >> b;ll ans = a;for (int j = 1; j <= n; j++){cin >> a >> b;ans *= a;ans%=mod;bmin = min(bmin, b);}cout << ans * fastPower(bmin,mod-2)%mod;
}
int main()
{solve();return 0;
}

E.学以致用

考察算法:
dfs,最优二叉树
思路:
在建立哈夫曼树的过程中加边,然后用dfs搜索叶子节点同时累计答案。

代码

#include<iostream>
#include<queue>
using namespace std;
#define endl "\n"
#define ull unsigned long long
#define ll  long long
#define INF 0x3f3f3f3f
const ll N = 1e5 + 5;
const ll mod = 1000000007;
const long double eps = 1e-6;
int head[N];
struct Edge {int to, next;
}e[105];
int cnt = 0;
void add(int u, int v) {e[++cnt].to = v;e[cnt].next = head[u];head[u] = cnt;
}
int ans = 0;
void  dfs(int s, int c) {if (head[s] == 0)ans += s * c;for (int i = head[s]; i; i = e[i].next) {dfs(e[i].to, c + 1);}
}
void solve() {int  n;cin >> n;priority_queue<int, vector<int >, greater<int > > q;for (int i = 1; i <= n; i++) {int tmp;cin >> tmp;q.push(tmp);}for (int i = 1; i < n; i++) {int a = q.top();q.pop();int b = q.top();q.pop();q.push(a + b);add(a + b, a);add(a + b, b);}dfs(q.top(), 0);cout << ans << endl;
}int main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);solve();return 0;
}

F.平稳变化

考察算法:
构造
思路:
首先考虑什么情况下会输出"Impossible",当且仅当存在 iii 使得pi与pi+1p_i与p_{i+1}pi​与pi+1​间可以存在的数字数量小于ai与ai+1a_i与a_{i+1}ai​与ai+1​间的数字数量,即存在 iii 使得pi+1−pi<abs(ai+1−ai)p_{i+1}-p_i < abs(a_{i+1}-a_i)pi+1​−pi​<abs(ai+1​−ai​)
接下来我们尝试构造该序列的最大可能数字,观察到,若第3个数为3,第7个数字也为3,我们可以构造第四个数为4,第五个数为5,第六个数字为4。得出3-7之间的最大值为5,也就是说,对于相邻的两个pi和pi+1p_i和p_{i+1}pi​和pi+1​,我们尝试先增后减,每个区间得到一个最大值,容易证明这是正确的。
最后不要忘记考虑头和尾,将这 m+1m + 1m+1个区间的最大值比较,得出整个数列的最大值,即为所求。
时间复杂度:O(m)O(m)O(m)

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n, m;
int p[maxn], a[maxn];
int main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);cin >> n >> m;int ans = 0;for(int i = 1; i <= m; i++) {cin >> p[i] >> a[i];ans = max(ans, a[i]);}ans = max(ans, a[1] + p[1] - 1);ans = max(ans, a[m] + n - p[m]);for(int i = 1; i < m; i++) {if(p[i+1]-p[i] < abs(a[i+1]-a[i])) {cout << "IMPOSSIBLE" << '\n';return 0;}if(p[i+1] >= p[i]) {ans = max(ans, (p[i+1]-p[i]+a[i+1]-a[i])/2+a[i]);}else {ans = max(ans, (p[i]-p[i+1]+a[i]-a[i+1])/2+a[i+1]);}}cout << ans << endl;return 0;
}

G.拜登打牌

考察算法:
二维偏序,树状数组
思路:
本题是一个典型的二维偏序题目,如果有选手不明白二维偏序,可以参考一下这篇文章——二维偏序介绍。简单来说,二维偏序就是定义了一个二维的“小于”关系,因为要把每个数分级,所以自然而然的我们考虑排序,问题的关键就在于如何排序。这里我们按照a的大小做为第一关键字,b的大小做为第二关键字(即a相同时,b小的在前面,否则a小的在前面)排序。这样排序的目的就是把所有可能小于当前点的点都在当前点前面被处理。
之后我们遍历排序之后数组的每项的b值,显然比第 iii 项小的数量等于前 i−1i-1i−1项中b比第 iii 项的b小的项的数量(因为第 iii 项的a一定大于前 i−1i-1i−1项)
举个例子,比如
(1,1),(2,3),(2,7),(3,8)(4,4),(4,6),(5,1)(1, 1),(2,3),(2,7),(3, 8)(4,4),(4,6),(5,1)(1,1),(2,3),(2,7),(3,8)(4,4),(4,6),(5,1)
对于(4,4)(4,4)(4,4),它后面的两项一定比它大,而它前面的(1,1),(2,3)(1, 1),(2,3)(1,1),(2,3)比它小,而(2,7),(3,8)(2,7),(3,8)(2,7),(3,8)比它大,所以它是三级手牌。
现在我们的问题就变成了对每个数,找到已经遍历的数字中比它的b小的数的数量,然后再加上1,即是它的级数。我们可以建立一个新数组cntcntcnt,每遍历一个b,就把这个数组第b项加1,比如上面的例子中,前五项cnt[1]=cnt[3]=cnt[4]=cnt[7]=cnt[8]=1cnt[1]=cnt[3]=cnt[4]=cnt[7]=cnt[8]=1cnt[1]=cnt[3]=cnt[4]=cnt[7]=cnt[8]=1,其余项为0,所以比4小(或等于)的数量即为cnt[0]+cnt[1]+cnt[2]+cnt[3]+cnt[4]=3cnt[0]+cnt[1]+cnt[2]+cnt[3]+cnt[4]=3cnt[0]+cnt[1]+cnt[2]+cnt[3]+cnt[4]=3, 但这相当于我们对每一项都需要O(n)遍历O(n)遍历O(n)遍历,总时间复杂度为O(n2)O(n^2)O(n2),能不能优化呢?
树状数组可以很好的解决这个问题。树状数组也是一种十分优美的数据结构,可以在O(logn)O(logn)O(logn)的时间复杂度对前缀和进行查询or对单点进行修改,也就是说,查询cnt[0]+cnt[1]+..cnt[n]cnt[0]+cnt[1]+..cnt[n]cnt[0]+cnt[1]+..cnt[n] or cnt[bi]cnt[b_i]cnt[bi​]++ 的时间复杂度从O(n)O(n)O(n) 优化到 O(logn)O(logn)O(logn), 总时间复杂度O(nlogn)O(nlogn)O(nlogn),强烈建议不懂的选手学习一下。或许选手不需要知道它的数学原理,只需要会用就可以了。
代码中有一个小细节,就是压入树状数组时要把b的值+1,因为b可能取0,也就是可能出现 cnt[0]cnt[0]cnt[0] 而使用树状数组的话,可以理解为下标必须至少为1,所以就把所有数都+1。

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e4 + 10;
const int maxm = 5e4 + 10;
int n;
int C[maxm], ans[maxn];
//lowbit(),query(),update()为树状数组的三个函数
int lowbit(int x) {return x & (-x);
}
int query(int x) {    //前x组的和int ret = 0;while(x > 0) {ret += C[x];x -= lowbit(x);}return ret;
}
void update(int x, int d) {    //第x组加dwhile(x <= maxm) {C[x] += d;x += lowbit(x);}
}
struct node {int a, b;bool operator < (const node &x) const {    //排序,按照a为第一关键字if(a == x.a) return b < x.b;      //b为第二关键字,从小到大return a < x.a;}
}p[maxn];
int main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);cin >> n;for(int i = 1; i <= n; i++)cin >> p[i].a >> p[i].b;sort(p + 1, p + n + 1);for(int i = 1; i <= n; i++) {update(p[i].b + 1, 1);ans[query(p[i].b + 1)]++;}for(int i = 1; i <= n; i++) cout << ans[i] << '\n';cout << '\n';return 0;
}

Note:
本题保证了没有重复卡牌,如果有重复卡牌的话这份代码还仍然正确吗?感兴趣的选手可以思考一下。我都这么问了当然是不正确的,然而只需要稍加修改即可

H. An Easy Problem

考察算法:
组合数学,乘法逆元
思路:
1,2,...,n−1,n1,2,...,n-1,n1,2,...,n−1,n 的全排列一共有 n!n!n! 种,每种全排列对应的对数一共有C(n,2)=n∗(n−1)/2C(n, 2) = n * (n - 1) / 2C(n,2)=n∗(n−1)/2 种。对于其中的每一对数,逆序对和“正序对”出现的次数显然是相等的,故除以222即可得到最终答案。 注意取模即可。最终答案为:
ans=n!∗n∗(n−1)/4ans = n! * n * (n - 1) / 4ans=n!∗n∗(n−1)/4
这个方法可能听起来有点抽象,举个例子,比如1-5的全排列,显然形如{x,x,3,2,x}的一共有3!=63!= 63!=6 种,同时,形如{x,x,2,3,x}的也同样有6种,而前者相当于3,2处在第三、四个位置上贡献了6个逆序对,后者则为0个。容易得到对于任意的两个数都是类似的,于是得到了上述公式。

时间复杂度:预处理O(n)O(n)O(n),对每个询问O(1)O(1)O(1)回答

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define LL long long
const int maxn = 1e6 + 10;
const int P = 998244353;
LL power(LL a, LL b) {LL ans = 1 % P;for (; b; b >>= 1) {if (b & 1)  ans = ans * a % P;a = a * a % P;}return ans;
}
LL inv(int a) { //费马小定理求逆元return power(a, P - 2);
}
int fac[maxn];
void init() {   //预处理阶乘fac[0] = 1;for(int i = 1; i < maxn; i++) {fac[i] = fac[i-1] * i % P;}
}
int z = inv(4);
void solve() {int n;cin >> n;int ans = fac[n];ans = ans * n % P * (n - 1) % P * z % P;cout << ans << '\n';
}
signed main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);init();int T; cin >> T; while(T--)solve();return 0;
}

I.求求你啦,帮帮可莉吧

考察算法:
进制与整除,同余
灵感来源于一场巴西的Regional,Creating Multiples,与本题唯一区别就是原题 mod=k+1mod=k+1mod=k+1,而本题为 mod=k−1mod = k - 1mod=k−1,事实上本题是该题的简化版,但方法类似,只是规律更容易发现,感兴趣的选手可以尝试一下原题。
思路:
经过观察得,ki≡1(mod(k−1))∀k,i∈N(k>1,i>0)k^{i}≡ 1 (mod (k-1))\quad\forall k, i\in\mathbb N(k > 1,i > 0) ki≡1(mod(k−1))∀k,i∈N(k>1,i>0)
我们知道,对于一个 kkk 进制数,它的第 iii 位(从低向高) aia_iai​可以表示成ai∗ki−1a_i*k^{i-1}ai​∗ki−1,所以我们得到ai∗ki−1≡ai(mod(k−1))∀k,i∈N(k>1,i>1)a_i * k^{i-1}≡ a_i (mod (k-1))\quad\forall k, i\in\mathbb N(k > 1, i > 1)ai​∗ki−1≡ai​(mod(k−1))∀k,i∈N(k>1,i>1)
也就是说,设KKK进制数BBB为a1,a2,...ana_1,a_2,...a_na1​,a2​,...an​,则
B≡Σi=1nai(mod(k−1))B ≡ Σ_{i=1}^na_i (mod (k - 1))B≡Σi=1n​ai​(mod(k−1))
所以我们可以先通过对aia_iai​求和,计算出它模k−1k-1k−1的余数sss,然后从高位向低位遍历,找到第一个不小于sss的数,将这一位减去sss,得到的即为模k−1k-1k−1余0的数,显然这是最小的。(不可能减去2∗s2*s2∗s,虽然后者如果可行的话更小,感兴趣的选手可以思考一下为什么)
时间复杂度O(l)O(l)O(l) ,lll为数字的位数。

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int k, l;
int a[maxn];
int main() {scanf("%d%d", &k, &l);int yu = 0;int mod = k - 1;for(int i = l; i >= 1; i--) {scanf("%d", &a[i]);yu = (yu + a[i]) % mod;}if(!yu)printf("0\n");else {for(int i = l; i >= 1; i--) {if(a[i] < yu) continue;printf("%d %d\n", l - i + 1, a[i] - yu);return 0;}printf("-1\n");}return 0;
}

J.嘉然小姐的魔法通道

考察算法:
并查集,二分答案
思路:
如果大家没有学习过并查集,建议先去了解一下并查集——一种非常经典的数据结构,本题只需要用到并查集的两个最基本的应用——查找两个元素是否处在一个集合&合并两个元素所在的两个集合。并查集讲解

本题改编自P3958 [NOIP2017 提高组] 奶酪,建议大家先把原题搞懂,洛谷的题解讲的十分到位,鄙人就不多赘述了。在原题的基础上,我们引入了“每隔1秒,魔法球的半径都会增长1”这一条件,所以考虑通过二分时间的方法来解决,具体题解见代码注释。
如果大家对二分答案部分有疑问,强烈建议学会这一方法。二分答案讲解
时间复杂度O(n2logn)O(n^2logn)O(n2logn)

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e3 + 10;
const int INF = 0x7f7f7f7f;
int n, e;
int x[maxn], y[maxn], r[maxn];
int fa[maxn];
int find(int x) {   //并查集if(x == fa[x])return x;return fa[x] = find(fa[x]);
}
void merge(int i, int j) { //并查集int fx = find(i);int fy = find(j);fa[fx] = fy;
}
bool judge(int i, int j, int t) {//计算t秒后i, j两点是否相互可达double dis = sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); //距离double now = r[i] + r[j] + (t << 1); //半径和if(now >= dis)return true;    //两点可达return false;   //两点不可达
}
bool ok(int t) {for(int i = 0; i <= n + 1; i++) {   //每次均要初始化父节点fa[i] = i;}for(int i = 1; i <= n; i++) {if(abs(x[i]-e) <= t + r[i])  //如果第i个点可以到达终点,则合并merge(i, n + 1);if(abs(x[i]-0) <= t + r[i]) //如果第i个点可以到达起点,则合并merge(i, 0);for(int j = 1; j < i; j++)if(judge(i, j, t))  //如果第i个点和第j个点可以相互到达,则合并merge(i, j);}int fx = find(0);int fy = find(n + 1);if(fx == fy)return true;    //如果起点和终点在同一个集合里,即相互可达,return truereturn false;      //否则return false
}
signed main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);cin >> n >> e;for(int i = 0; i <= n + 1; i++)fa[i] = i;for(int i = 1; i <= n; i++) {cin >> x[i] >> y[i] >> r[i];}x[0] = 0; y[0] = 0; r[0] = 0;       //起点x[n + 1] = e; y[n + 1] = 0; r[n + 1] = 0; //终点int L = 0, R = INF; //实际上限为sqrt(y^2 + max(e^2,1e9-e^2)),方便起见直接设为INF。int ans = INF;while(L <= R) { //二分答案int mid = (L + R) >> 1;if(ok(mid)) {R = mid - 1;ans = mid;}elseL = mid + 1;}cout << ans << '\n';return 0;
}

K.嘉然小姐的魔法日记

考察算法:
主席树(可持久化线段树)
思路:
出题人做法:对原序列建主席树,由于年份不是连续的,离散化处理一下即可。对主席树有兴趣的选手如果还不会的话可以参考主席树模板中的题解,鄙人在此就不解释了。
更简单的做法:
比赛时发现大部分AC代码都是用离线做法,这波出题人表示也没有想到。
下面附上队友写的主席树std做为参考。

代码

#include<iostream>
#define maxn 400500
#define mid (l+r)/2
using namespace std;
typedef struct hjtree{int l,r;int val;
}hjtree;
int n,m;
int a[maxn];
int cnt=0,root[2*maxn];
int tmp=0;
int searcher[2*maxn];
hjtree t[(2*maxn)<<6];
void init(int &o,int l,int r){o=++cnt;if(l==r){t[o].val=a[l];return;}init(t[o].l,l,mid);init(t[o].r,mid+1,r);return;
}
void build(int &o,int pre,int l,int r,int pos,int val){o=++cnt;t[o]=t[pre];if(l==r){t[o].val=val;return;}if(pos<=mid)build(t[o].l,t[pre].l,l,mid,pos,val);else build(t[o].r,t[pre].r,mid+1,r,pos,val);
}int query(int o,int l,int r,int pos){if(l==r)return t[o].val;if(pos<=mid)return query(t[o].l,l,mid,pos);else return query(t[o].r,mid+1,r,pos);
}int main()
{ios::sync_with_stdio(false);cin.tie(0);cin>>n>>m;for(int i=1;i<=n;i++)cin>>a[i];init(root[0],1,n);for(int i=1;i<=m;i++){int op;cin>>op;if(op==0){int y,p1,p2;cin>>y>>p1>>p2;int val1=query(root[tmp],1,n,p1),val2=query(root[tmp],1,n,p2);tmp++;searcher[tmp]=y;build(root[tmp],root[tmp-1],1,n,p1,val2);tmp++;searcher[tmp]=y;build(root[tmp],root[tmp-1],1,n,p2,val1);}else if(op==1){int y,p;cin>>y>>p;int version=(lower_bound(searcher,searcher+tmp+1,y)-searcher);if(version!=0){if(searcher[version]>y)version--;else version++;}cout<<query(root[version],1,n,p)<<"\n";}}return 0;
}

L.七海の新春旅行

考察算法:
动态规划,背包问题
思路:
思路比较清晰,由于背包尽量填满,于是初始时背包只有容量为0时能用,于是可以设背包的其它值为-1,然后正常跑完全背包求出每个行李箱容量下的最大价值,然后进行一次01背包,选出重量不超过 mmm 的情况下产生的最大价值。但这题的关键在于由于 nnn 的范围很大,在若直接进行背包会超时,注意到最多只存在m个本质相同的行李箱,于是可以按照多重背包的思路进行二进制优化或单调队列优化,即可解决此题。
时间复杂度O(m∗k+m∗m∗logn)O(m*k+m*m*logn)O(m∗k+m∗m∗logn)
本来想把gi和hig_i和h_igi​和hi​的范围都设为 mmm 的范围也就是 ≤2000\leq 2000≤2000,然而出题人说由于数据太大python跑不出来于是定为了300,好像又被选手卡过去了。

代码

#include<iostream>
using namespace std;
int m,n,k;
int g[200500];
int h[2050];
long long v[2050];
int mp[2550],cnt[2550];
long long f1[2550],f2[2550];
int nw[200500];
long long nv[200500];
int tmp=0;
int main() {ios::sync_with_stdio(false);cin.tie(0);cin>>m>>n>>k;for(int i=1;i<=n;i++)cin>>g[i];for(int i=1;i<=k;i++)cin>>h[i]>>v[i];for(int i=1;i<=m;i++)f1[i]=-1;f1[0]=0;for(int i=1;i<=k;i++){for(int j=h[i];j<=m;j++){if(f1[j-h[i]]!=-1)f1[j]=max(f1[j],f1[j-h[i]]+v[i]);}}for(int i=0;i<=m;i++)mp[i]=i;for(int i=1;i<=m;i++){if(f1[i]==-1)mp[i]=mp[i-1];}for(int i=1;i<=n;i++){if(g[i]<=m)cnt[mp[g[i]]]++;}for(int i=1;i<=m;i++){if(f1[i]==-1)continue;for(int j=1;j<=cnt[i];j<<=1){nw[++tmp]=j*i;nv[tmp]=j*f1[i];cnt[i]-=j;}if(cnt[i]){nw[++tmp]=cnt[i]*i;nv[tmp]=cnt[i]*f1[i];}}for(int i=1;i<=tmp;i++){for(int j=m;j>=nw[i];j--){f2[j]=max(f2[j],f2[j-nw[i]]+nv[i]);}}cout<<f2[m];return 0;
}

M.嘉然小姐的新春礼物

**考察算法:**状压dp
思路:
注意到 nnn 的范围在16以内,容易想到使用状压dp解决此题,首先状压枚举礼物,求出每组礼物被一个套环圈住的最小圆,由于 mmm 比较大,容易想到二分查找最小的大于所求圆的圆环,但是数据不一定是递增的,即更大的圆环的价值可能更小,因此首先预处理,贪心地舍弃不符合规则的圆环,然后就可以二分求解了,这样得到若干个礼物被一个圆环套住的最小价值,然后就可以枚举子集状压dp求解最终结果了,时间复杂度O(2n∗logm+3n)O(2^n*logm+3^n)O(2n∗logm+3n)
下面给出队友写的std

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
const int maxn = 1e9+7;
using namespace std;
struct vec
{double x, y;vec (const double& x0 = 0, const double& y0 = 0) : x(x0), y(y0) {}vec operator + (const vec& t) const {return vec(x+t.x, y+t.y);}vec operator - (const vec& t) const {return vec(x-t.x, y-t.y);}vec operator * (const double& t) const {return vec(x*t, y*t);}vec operator / (const double& t) const {return vec(x/t, y/t);}const double len2 () const {return x*x + y*y;}const double len () const {return sqrt(len2());}vec norm() const {return *this/len();}vec rotate_90_c () {return vec(y, -x);}
};
typedef struct loop{int rad,p;
}loop;
int n,m;
vec potn[25];
vec pot[25];
loop ring1[200500];
int v[200500];
loop ring2[200500];
int tmp=0;
int mp[200500];
int f[(1<<20)];
int cmp(loop l1,loop l2){if(l1.rad==l2.rad)return l1.p>l2.p;else return l1.rad<l2.rad;
}
double dot(const vec& a, const vec& b) {return a.x*b.x + a.y*b.y;}
double crs(const vec& a, const vec& b) {return a.x*b.y - a.y*b.x;}
vec lin_lin_int(const vec& p0, const vec& v0, const vec& p1, const vec& v1)
{double t = crs(p1-p0, v1) / crs(v0, v1);return p0 + v0 * t;
}
vec circle(const vec& a, const vec& b, const vec& c)
{return lin_lin_int((a+b)/2, (b-a).rotate_90_c(), (a+c)/2, (c-a).rotate_90_c());
}
double getcircle(int siz){random_shuffle(pot+1, pot+siz+1);vec o;double r2 = 0;for(int i=1; i<=siz; i++){if((pot[i]-o).len2() > r2){o = pot[i], r2 = 0;for(int j=1; j<i; j++){if((pot[j]-o).len2() > r2){o = (pot[i]+pot[j])/2, r2 = (pot[j]-o).len2();for(int k=1; k<j; k++){if((pot[k]-o).len2() > r2){o = circle(pot[i], pot[j], pot[k]), r2 = (pot[k]-o).len2();}}}}}}return sqrt(r2);
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cin>>n>>m;for(int i=1;i<=n;i++)cin>>potn[i].x>>potn[i].y;for(int i=1;i<=m;i++)cin>>ring1[i].rad>>ring1[i].p;sort(ring1+1,ring1+m+1,cmp);int minl=maxn;for(int i=m;i>=1;i--){if(ring1[i].p>=minl)v[i]=1;minl=min(minl,ring1[i].p);}for(int i=1;i<=m;i++){if(!v[i])ring2[++tmp]=ring1[i];}tmp++;ring2[tmp].rad=maxn,ring2[tmp].p=maxn;for(int i=1;i<=tmp;i++)mp[i]=ring2[i].rad;for(int i=1;i<=(1<<n)-1;i++)f[i]=maxn;for(int i=1;i<=(1<<n)-1;i++){int siz=0;int h=i,cnt=1;while(h){if((h&1)){pot[++siz]=potn[cnt];}cnt++;h>>=1;} double rr=getcircle(siz);f[i]=ring2[lower_bound(mp+1,mp+tmp+1,rr)-mp].p;}for(int i=1;i<=(1<<n)-1;i++){for(int j=i;j!=0;j=((j-1)&i)){f[i]=min(f[i],f[i-j]+f[j]);}}cout<<f[(1<<n)-1];return 0;
}

大工之星编程挑战赛第五周题解相关推荐

  1. 大工之星编程挑战赛第一周题解

    大工之星编程挑战赛第一周题解 总结 去年出了一车BUG,今年希望举办的时候希望没有BUG.最后看来,榜看起来还是很好看的,没有人AK,前排过的很多,后排也有题过.榜单也没有出现断层,算是成功了. 其实 ...

  2. 中国大学MOOC C语言程序设计(大连理工大学) 课后编程题 第五周题解(个人向仅供参考)

    第5周 循环结构程序设计 1 用for语句实现判断并求和.(10分) 题目内容: 求1~100以内 ( 包括100)能被3整除同时被5整除余数为1的所有数之和.要求用for语句完成. 输入格式: 无 ...

  3. C语言编程>第五周 ⑤ 编写一个程序,从键盘输入X,Y,Z 3个数, 编写函数计算3个数的立方和并返回计算结果。

    例题:编写一个程序,从键盘输入X,Y,Z 3个数, 编写函数计算3个数的立方和并返回计算结果. 第一种方法: 代码如下: #include <stdio.h> int abc(int,in ...

  4. C语言编程>第五周 ① 目前人民币共有以下几种面值(不包括角和): 1元 2元 5元 10元 20元 50元 100元 编写一个函数计算金额“X“需要多少张面值为n 元的纸币。

    例题:目前人民币共有以下几种面值(不包括角和):1元 2元 5元 10元 20元 50元 100元 编写一个函数计算金额"X"需要多少张面值为n 元的纸币. 代码如下: /*代码分 ...

  5. CSDN编程挑战赛第六期—题解(另附:最长上升子序列分析题解)

    CSDN编程竞赛报名地址:https://edu.csdn.net/contest/detail/16 (请不要删掉此地址) "路漫漫其修远兮,吾将上下而求索" 前言 笔者属于刚入 ...

  6. 中国大学MOOC C语言程序设计(大连理工大学) 课后编程题 第十周题解(个人向仅供参考)

    第10周 变量的作用域及编译预处理 1 求x的n次方(10分) 题目内容:编写求x的n次方的递归函数,在主函数调用并输出.(x为double型,n为整型,函数类型为double型) 输入格式: %lf ...

  7. 中国大学MOOC C语言程序设计(大连理工大学) 课后编程题 第十一周题解(个人向仅供参考)

    第11周 指针一 1 利用指针编写程序,统计字符串的长度.(10分) 题目内容:利用指针编写程序,统计字符串的长度.(不要使用求字符串长度函数) 输入格式: gets() 输出格式: "Le ...

  8. 中国大学MOOC C语言程序设计(大连理工大学) 课后编程题 第十三周题解(个人向仅供参考)

    第十三周 结构与共用体 1 某班有5名同学,建立一个学生的简单信息表,包括学号.姓名.3门课程的成绩,编写程序,计算每名学生的平均成绩及名次.(30分) 题目内容: 某班有5名同学,建立一个学生的简单 ...

  9. 中国大学MOOC C语言程序设计(大连理工大学) 课后编程题 第三周题解(个人向仅供参考)

    第3周 数据的输入与输出 题1 交换两个变量(5分) 题目内容: 编写程序, 输入两个整型变量,交换两个变量的值,输出交换后的变量 输入格式: %d,%d 输出格式: %d,%d 输入样例: 3,4 ...

  10. 【算法】第三届全国大学生算法设计与编程挑战赛(冬季赛)

    7题金,6题银,5题铜 [参考:2021-2022年度第三届全国大学生算法设计与编程挑战赛(冬季赛)题解_int 我的博客-CSDN博客] [参考:2021-2022年度第三届全国大学生算法设计与编程 ...

最新文章

  1. 收集一些好的技术文档
  2. spring实战六之使用基于java配置的Spring
  3. .net EF监控 MiniProfiler
  4. [spring-framework]Spring定时器的配置和使用
  5. 实惨!连各大编程语言都摆起地摊了!
  6. linux循环控制结构,Linux Shell 之 Shell 基本控制结构(二)(循环结构)
  7. axios介绍---axios工作笔记001
  8. 2019 已过半,迅雷链的 Flag 完成了多少?
  9. SpringMVC基础学习(二)—开发Handler
  10. 从源码角度深入理解LayoutInflater
  11. 安卓第十三天笔记-服务(Service)
  12. 基于SpringBoot的简单记账系统
  13. 纯电动汽车架构设计(一) :电动车架构设计核心与前悬架选择
  14. php主机字节序和网络字节序
  15. html用360浏览器的好处,IE、360、百度等主流浏览器使用性能比较
  16. 怎么更改苹果账户名称_苹果新品快来了,你真的学会如何找回你的Apple ID的密码了吗?...
  17. GitHub置顶半个月!78w字百亿级并发设计(全彩PDF),竟出自京东
  18. Android 中图片占用内存分析
  19. 基于C++和QT实现的个人通讯录管理系统
  20. slope one predictors

热门文章

  1. 电脑自动安装软件、各种弹窗广告、中病毒等问题解决方案
  2. 基于一维卷积神经网络模型的AI量化智能选股策略
  3. 电源压敏电阻的计算选择
  4. studio3t破解
  5. Socket 简介及面试题
  6. 全新一代智慧园区数字孪生解决方案,为园区运营商和集成商赋能
  7. 房地产数字化营销方案-蓝图
  8. 【阿里云生活物联网架构师专题 ⑥】ESP8266接入阿里生活飞燕平台国际版,实现亚马逊Alexa Echo音响语音控制;
  9. python与开源_Python与开源GIS
  10. css实现椭圆绕圈动画