这可能是我做
d
i
v
2
div2
div2以来做的最好的一次?它居然
u
n
r
a
t
e
d
unrated
unrated了!哼╭(╯^╰)╮不给力的服务器!关键时刻拓机!虽然这次的D比较水。但是是我一次过了四题QAQ…
比赛传送门:https://codeforces.com/contest/1106
A. Lunar New Year and Cross Counting
题解:数交叉 X X X个数, O ( N 2 ) O(N^2) O(N2)枚举中点即可。
代码
#include<bits/stdc++.h>
using namespace std;
string s[505];
int n;
int count1(int x,int y)
{
if(s[x][y] != 'X') return 0;
int dir[4][2] = {-1,-1,-1,1,1,-1,1,1};
for(int i = 0; i < 4; ++i) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if(tx < 0 || tx >= n || ty < 0 || ty >= n)
return 0;
if(s[tx][ty] != 'X') return 0;
}
return 1;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.in","r",stdin);
#endif
cin >> n;
for(int i = 0; i < n; ++i) {
cin >> s[i];
}
int ans = 0;
for(int i = 0; i < n; ++i) {
for(int j = 0; j < n; ++j) {
ans += count1(i,j);
}
}
cout << ans << endl;
return 0;
}
B. Lunar New Year and Food Ordering
题解:按题意模拟,用一个优先队列维护当前最便宜并且 i n d e x index index最小的食物,如果当前顾客所需要的食物数量不够,那就取 p o p pop pop出来最便宜的食物,直到满足当前顾客,如果满足不了那就输出 0 0 0;如果足够当前顾客需要的,那就直接输出数量乘以花费即可。( P s : Ps: Ps:千万要注意会爆long long!!)
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1E5+10;
struct node{
int t,d;
}cust[N];
struct food{
int c,d,id;
bool operator < (const food & u) const {
if(d == u.d) {
return id > u.id;
}
return d > u.d;
}
};
priority_queue<food> pq;
int c[N],d[N];
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.in","r",stdin);
#endif
int n,m;
cin >> n >> m;
for(int i = 0; i < n; ++i) {
scanf("%d",&c[i]);
}
for(int i = 0; i < n; ++i) {
scanf("%d",&d[i]);
}
for(int i = 0; i < n; ++i) {
pq.push(food{c[i],d[i],i});
}
for(int i = 0; i < m; ++i) {
scanf("%d%d",&cust[i].t,&cust[i].d);
cust[i].t--;
}
long long tmp = 0;
for(int i = 0; i < m; ++i) {
tmp = 0;
int t = cust[i].t;
int num = cust[i].d;
if(num > c[t]) {
tmp += 1LL * d[t] * c[t];
num -= c[t];
c[t] = 0;
if(pq.empty()) tmp = -1;
while(!pq.empty()) {
if(tmp == -1) break;
food cur = pq.top(); pq.pop();
cur.c = min(cur.c,c[cur.id]);
if(cur.c >= num) {
tmp += 1LL * cur.d * num;
cur.c -= num;
c[cur.id] -= num;
if(cur.c != 0)
pq.push(cur);
break;
}else{
tmp += 1LL * cur.c * cur.d;
num -= cur.c;
c[cur.id] = 0;
}
if(pq.empty()) tmp = -1;
}
}else{
c[t] -= num;
tmp += 1LL * d[t] * num;
}
if(tmp == -1) tmp = 0;
cout << tmp << endl;
}
return 0;
}
C. Lunar New Year and Number Division
说实话这题当时纯靠莽…,题意是给偶数个数,然后分组,每组至少两个,最后求最小化 ∑ j = 1 m s j 2 \sum_{j = 1}^{m}{s_j}^2 ∑j=1msj2, s j s_j sj为第 j j j组的数字的和,一共有 m m m组。既然要最小化和,那么就要最小化 s j 2 {s_j}^2 sj2,我当时就猜测,肯定每组两个数,让每组最小的话肯定是最小和最大配,第二小和第二大配。然后莽了一发就A了。
题解:我们可以考虑两个数,
a
,
b
a,b
a,b,答案只能是
(1)
(
a
+
b
)
2
(a + b)^2\tag 1
(a+b)2(1)四个数,
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d分一组
(2)
(
a
+
b
+
c
+
d
)
2
=
(
a
+
b
)
2
+
(
c
+
d
)
2
+
2
(
a
+
b
)
(
c
+
d
)
(a + b + c + d)^2 = (a+b)^2+(c+d)^2+2(a+b)(c+d)\tag 2
(a+b+c+d)2=(a+b)2+(c+d)2+2(a+b)(c+d)(2)分两组
(3)
(
a
+
b
)
2
+
(
c
+
d
)
2
(a + b)^2+(c+d)^2 \tag 3
(a+b)2+(c+d)2(3)
对比
(
2
)
(
3
)
(2)(3)
(2)(3)显然每组的数量越少,总和越小。我们再观察
(
3
)
(3)
(3),拆开:
(4)
a
2
+
b
2
+
c
2
+
d
2
+
2
(
a
b
+
c
d
)
a^2+b^2+c^2+d^2+2(ab+cd)\tag 4
a2+b2+c2+d2+2(ab+cd)(4)
如果我们想要
(
4
)
(4)
(4)尽可能的小,那么我们就需要最小化
a
b
ab
ab和
c
d
cd
cd,由乘法的性质可得让
a
b
s
(
a
−
b
)
abs(a-b)
abs(a−b)和
a
b
s
(
c
−
d
)
abs(c-d)
abs(c−d)尽可能大即可。因此我们可以证明最小的和最大的配,第二小和第二大的配,以此类推的做法是正确的。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 3E5+10;
long long a[N];
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.in","r",stdin);
#endif
int n;
cin >> n;
for(int i = 0; i < n; ++i) {
scanf("%lld",&a[i]);
}
sort(a,a+n);
long long sum = 0;
for(int i = 0; i < n / 2; ++i) {
sum += (a[i] + a[n - i - 1]) * (a[i] + a[n - i - 1]);
}
cout << sum << endl;
return 0;
}
D. Lunar New Year and a Wander
题解:既然要访问的路径字典序最小,那么我们用优先队列+bfs就可以很好解决了。
代码
#include<bits/stdc++.h>
#define P pair<int,int>
using namespace std;
const int N = 1E5+10;
//vector<int> e[N];
priority_queue<int,vector<int>,greater<int> > e[N];
bool vis[N];
vector<int> ans;
priority_queue<int,vector<int>,greater<int> > pq;
void bfs()
{
pq.push(1);
vis[1] = 1;
ans.push_back(1);
while(!pq.empty()) {
int cur = pq.top();
if(vis[cur] == 0) {
vis[cur] = 1;
ans.push_back(cur);
}
pq.pop();
for(; !e[cur].empty(); e[cur].pop()) {
int v = e[cur].top();
pq.push(v);
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.in","r",stdin);
#endif
int n,m,x,y;
cin >> n >> m;
for(int i = 0; i < m; ++i) {
scanf("%d%d",&x,&y);
e[x].push(y);
e[y].push(x);
}
bfs();
int len = ans.size();
for(int i = 0; i < len; ++i) {
printf("%d%c",ans[i],i==len-1?'\n':' ');
}
return 0;
}
E. Lunar New Year and Red Envelopes
题意: B o b Bob Bob在每个 [ s i , t i ] [s_i,t_i] [si,ti]时间段内抢红包,只要在当前时间 x x x可以抢红包,那么他就一定会去抢。然后没抢完一个红包,那么他必须等到 d i + 1 d_i+1 di+1时间才能抢下一个红包。期间 A l i c e Alice Alice最多可以 m m m次阻挠 B o b Bob Bob 在时间 x x x时抢红包。问 B o b Bob Bob最少可以获得多少红包。
题解:我们可以将问题分为两个部分,没有
A
l
i
c
e
Alice
Alice阻挠,
B
o
b
Bob
Bob所能获得的最少收益。和有
A
l
i
c
e
Alice
Alice不超过
m
m
m次时间点阻挠最少所能获得的收益。
那么第一部分是可以通过优先队列预处理出来的,同时我们记录每个时间点的收益,和下一次抢红包的时间点。然后对于不超过
m
m
m次的阻挠,我们可以用动态规划来解决。
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示在
i
i
i时间时
j
j
j次阻挠最少所能获得的收益。我们发现状态的转移有
i
−
1
i-1
i−1和
n
x
t
[
i
]
nxt[i]
nxt[i]两种,并且每次抢完红包必须等到
d
i
+
1
d_i+1
di+1才能抢,也就意味着下一次抢红包时间是大于你的决策时的时间的,因此我们不妨考虑倒过来转移。(实际上正着来也是可以的,而且官方题解是用的滚动数组)就有
{
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
n
x
t
[
i
]
]
[
j
]
+
g
o
t
[
i
]
)
d
p
[
i
]
[
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
+
1
]
[
j
−
1
]
)
\begin{cases}dp[i][j] =min(dp[i][j],dp[nxt[i]][j]+got[i])\\\\dp[i][[j] = min(dp[i][j],dp[i+1][j-1])\end{cases}
⎩⎪⎨⎪⎧dp[i][j]=min(dp[i][j],dp[nxt[i]][j]+got[i])dp[i][[j]=min(dp[i][j],dp[i+1][j−1])
最后答案就是
m
i
n
{
d
p
[
1
]
[
j
]
}
min\{dp[1][j]\}
min{dp[1][j]}。
代码
#include<bits/stdc++.h>
typedef long long LL;d
using namespace std;
const int N = 1E5+10;
struct node{
int s,t,d,w;
bool operator < (const node & u) const{
if(w == u.w) return d < u.d;
return w < u.w;
}
};
bool cmp(const node & a, const node & b){ return a.s < b.s; }
priority_queue<node> pq;
vector<node> v;
LL dp[N][201], got[N], nxt[N];
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.in","r",stdin);
#endif
int n,m,k,s,t,d,w;
cin >> n >> m >> k;
for(int i = 0; i < k; ++i) {
cin >> s >> t >> d >> w;
v.push_back(node{s,t,d,w});
}
sort(v.begin(),v.end(),cmp);
int cur = 0;
//1 4
//6 4 + 7
//11 4 + 7 + 9 <!> 11 <!>12 --> 4 + 7 = 11
for(int i = 1; i <= n; ++i) {
while(cur < k && v[cur].s <= i)
pq.push(v[cur++]);
while(!pq.empty() && pq.top().t < i) pq.pop();
if(!pq.empty()){
node t = pq.top();
got[i] = t.w;
nxt[i] = t.d + 1;
}else{
got[i] = 0;
nxt[i] = i + 1;
}
}
memset(dp,0x3f,sizeof dp);
dp[n + 1][0] = 0;
for(int i = n; i > 0; --i) {
dp[i][0] = dp[nxt[i]][0] + got[i];
}
for(int i = n; i >= 1; --i) {
for(int j = 0; j <= m; ++j) {
dp[i][j] = min(dp[i][j], dp[nxt[i]][j] + got[i]);
dp[i][j] = min(dp[i][j], dp[i + 1][j - 1]);
}
}
LL ans = *min_element(dp[1],dp[2]);
cout << ans << endl;
return 0;
}