2020.9.6 解题报告
2020.9.6
答题情况
总成绩 : 150 , 排名 : 5 / 6
T1 : 100 T2 : 20 T3 : 30
各题目分析
题目 1 :
预估成绩 : 100 实际成绩 : 100 考试用时 : 8:00 ~ 8:20
签到题。
题目 2 :
预估成绩 : 100 实际成绩 : 20 考试用时 : 8:20 ~ 9:00,10:00 ~ 11:00
先写了 \(O(n^3\log m)\) 的暴力,卡常卡到 2.4s 想出正解。
写完正解扔了,最后 FST 了。
错了两个边界问题,写完代码后一定通读前文检查!
题目 3 :
预估成绩 : 0 实际成绩 : 30 考试用时 : 9:00 ~ 10:00,11:00 ~ 11:30
一开始就觉得不可做,弃了,写了贪心。
题目解析
T1
将 n 个串中相同位置的 0/1 的数量记录下来。
贪心的找每一位数量较少的数字即可。
T2
二分最小的长度,DP 检查。
设 \(f_{i,j}\) 表示使用了 \(i\) 次红,\(j\) 次绿后,能覆盖到的最远的法坛的位置。
DP 方程:
\(\operatorname{red\_next}(x)\) 表示从 \(x\) 开始,使用一次红光,能覆盖到的最远的右侧的法坛,\(\operatorname{green\_next}\) 同理。
上述两值可以通过二分获得,也可以直接 \(O(n^2)\) 预处理。
最后检查能否覆盖到 \(n\) 即可,预处理后复杂度 \(O(n^2\log m)\)(\(m\) 为答案最大值)。
T3
要求次短路,先求出最短路。
设到达节点 \(x\) 的最短路长为 \(s1\), 与结点 \(x\) 相连的最短边长度为 \(c\)。
设 \(s2=s1+2\times c\),\(s2\) 即为次短路长度的上界。
DFS,搜索过程中利用 \(s2\) 进行剪枝,不断更新 \(s2\)。
可以证明每个节点被到达不超过 \(n\) 次。
代码实现
T1 :
考场代码
//
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
const int kMaxn = 1010;
//=============================================================
int n, l, cnt[kMaxn][2];
char s[kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
//=============================================================
int main() {
freopen("curse.in", "r", stdin);
freopen("curse.out", "w", stdout);
n = read();
for (int i = 1; i <= n; ++ i) {
scanf("%s", s + 1);
l = strlen(s + 1);
for (int i = 1; i <= l; ++ i) {
cnt[i][s[i] == '1'] ++;
}
}
for (int i = 1; i <= l; ++ i) {
printf("%d", cnt[i][1] > cnt[i][0]);
}
return 0;
}
T2:
考场代码
//
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
const int kMaxn = 2000 + 10;
const int kInf = 1e9 + 10;
//=============================================================
int n, r, g, a[kMaxn];
int f[kMaxn][kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void GetMax(int &fir, int sec) {
if (sec > fir) fir = sec;
}
int Query(int pos_, int val_) {
int l = pos_ + 1, r = n, ret;
while (l <= r) {
int mid = (l + r) >> 1;
if (a[mid] - a[pos_ + 1] <= val_) {
ret = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ret;
}
void koishi() {
int satoti;
}
bool Check(int l_) {
bool ret = false;
memset(f, 0, sizeof (f));
f[0][0] = - 1;
for (int i = 0; i <= r; ++ i) {
for (int j = 0; j <= g; ++ j) {
if (i) GetMax(f[i][j], Query(f[i - 1][j], l_));
if (j) GetMax(f[i][j], Query(f[i][j - 1], 2 * l_));
if(f[i][j] >= n) ret = true;
}
}
return ret;
}
//=============================================================
int main() {
freopen("light.in", "r", stdin);
freopen("light.out", "w", stdout);
n = read(), r = read(), g = read();
if (r + g >= n) {
printf("1\n");
return 0;
}
for (int i = 1; i <= n; ++ i) a[i] = read();
std :: sort(a + 1, a + n + 1);
int l = 2;
int r = a[n] / (r + g);
int ans = a[n] / (r + g);
while (l <= r) {
int mid = (l + r) >> 1;
if (Check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%d\n", ans);
return 0;
}
/*
3 1 0
10000
20000
30000
*/
/*
12 1 1
1
2
3
4
5
6
7
8
21
22
23
24
*/
//O(n^3\log m) 暴力
/*
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
const int kMaxn = 400 + 5;
const int kInf = 1e9 + 10;
//=============================================================
int n, r, g, a[5 * kMaxn];
bool f[5 * kMaxn][kMaxn][kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
int Query(int pos_, int val_) {
int l = 1, r = pos_, ret;
while (l <= r) {
int mid = (l + r) >> 1;
if (a[mid] > val_) {
ret = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
return ret;
}
void koishi() {
int satoti;
}
bool Check(int l_) {
memset(f, 0, sizeof (f));
f[0][0][0] = true;
bool ret = false;
for (int i = 1; i <= n; ++ i) {
int pre_red = Query(i, a[i] - l_) - 1;
int pre_green = Query(i, a[i] - 2 * l_) - 1;
int limj = std :: min(i, r), limk = std :: min(i, g);
for (int j = 0; j <= limj; ++ j) {
for (int k = 0; k <= limk; ++ k) {
if (j) f[i][j][k] |= f[pre_red][j - 1][k];
if (k) f[i][j][k] |= f[pre_green][j][k - 1];
if (i == n) ret |= f[i][j][k];
}
}
}
return ret;
}
//=============================================================
int main() {
// freopen("t2example.in", "r", stdin);
// freopen("light.out", "w", stdout);
n = read(), r = read(), g = read();
if (r + g >= n) {
printf("1\n");
return 0;
}
for (int i = 1; i <= n; ++ i) a[i] = read();
std :: sort(a + 1, a + n + 1);
int l = 2, r = a[n] / (r + g), ans = a[n] / (r + g);
while (l <= r) {
int mid = (l + r) >> 1;
if (Check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%d\n", ans);
return 0;
}
*/
正解
修改了两个边界问题的 \(O(n^2\log n \log m)\)
//
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
const int kMaxn = 2000 + 10;
const int kInf = 1e9 + 10;
//=============================================================
int n, r, g, a[kMaxn];
int f[kMaxn][kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void GetMax(int &fir, int sec) {
if (sec > fir) fir = sec;
}
int Query(int pos_, int val_) {
int l = pos_ + 1, r = n, ret;
while (l <= r) {
int mid = (l + r) >> 1;
if (a[mid] - a[pos_ + 1] + 1 <= val_) { //区间长度为 a[mid] - a[pos_ + 1] + 1
ret = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ret;
}
void koishi() {
int satoti;
}
bool Check(int l_) {
bool ret = false;
memset(f, 0, sizeof (f));
//f[0][0] 初值为 0.
for (int i = 0; i <= r; ++ i) {
for (int j = 0; j <= g; ++ j) {
if (i) GetMax(f[i][j], Query(f[i - 1][j], l_));
if (j) GetMax(f[i][j], Query(f[i][j - 1], 2 * l_));
if(f[i][j] >= n) ret = true;
}
}
return ret;
}
//=============================================================
int main() {
// freopen("light.in", "r", stdin);
// freopen("light.out", "w", stdout);
n = read(), r = read(), g = read();
if (r + g >= n) {
printf("1\n");
return 0;
}
for (int i = 1; i <= n; ++ i) a[i] = read();
std :: sort(a + 1, a + n + 1);
int l = 2;
int r = a[n];
int ans = a[n];
while (l <= r) {
int mid = (l + r) >> 1;
if (Check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%d\n", ans);
return 0;
}
官方 \(O(n^2 \log m)\) 做法
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, p, q, a[2001], l = 1, r = 0, ans = 0;
int dp[2001][2001], P[2001], Q[2001];
bool check(int L)
{
memset(dp, 0, sizeof(dp));
memset(P, 0, sizeof(P));
memset(Q, 0, sizeof(Q));
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++)
{
if (a[j] - a[i] + 1<= L) P[i] = j;
if (a[j] - a[i] + 1<= 2*L) Q[i] = j;
}
P[n+1] = Q[n+1] = n;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= q; j++)
{
if (i > 0) dp[i][j] = max(dp[i][j], P[dp[i-1][j] + 1]);
if (j > 0) dp[i][j] = max(dp[i][j], Q[dp[i][j-1] + 1]);
}
return dp[p][q] == n;
}
int main()
{
freopen("t2example.in", "r", stdin);
freopen("t2example.out", "w", stdout);
scanf("%d%d%d", &n, &p, &q);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
a[0] = 0;
r = a[n] - a[1] + 1;
if (p + q >= n) { printf("1\n"); return 0; }
while (l <= r)
{
int mid = (l+r) / 2;
if (check(mid)) ans = mid, r = mid -1; else l = mid + 1;
}
printf("%d\n", ans);
return 0;
}
T3:
考场代码
//
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <queue>
#include <cstring>
#define ll long long
const int kMaxn = 5010;
const int kMaxm = 2e5 + 10;
//=============================================================
int n, m, edge_num, head[kMaxn], v[kMaxm << 1], w[kMaxm << 1], ne[kMaxm << 1];
int dis[kMaxn], secdis[kMaxn];
bool vis[kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void AddEdge(int u_, int v_, int w_) {
v[++ edge_num] = v_, w[edge_num] = w_;
ne[edge_num] = head[u_], head[u_] = edge_num;
}
void Spfa() {
std :: priority_queue <int> q;
memset(dis, 63, sizeof (dis));
memset(secdis, 63, sizeof (secdis));
dis[1] = 0;
q.push(1);
vis[1] = true;
while (! q.empty()) {
int u_ = q.top(); q.pop();
vis[u_] = false;
for (int i = head[u_]; i; i = ne[i]) {
int v_ = v[i], w_ = w[i];
if (dis[u_] + w_ < dis[v_]) {
secdis[v_] = dis[v_];
dis[v_] = dis[u_] + w_;
if (secdis[u_] + w_ < secdis[v_]) {
secdis[v_] = secdis[u_] + w_;
}
if (! vis[v_]) {
q.push(v_);
vis[v_] = true;
}
} else if (dis[u_] + w_ < secdis[v_] && dis[u_] + w_ != dis[v_]) {
secdis[v_] = dis[u_] + w_;
if (! vis[v_]) {
q.push(v_);
vis[v_] = true;
}
}
}
}
}
//=============================================================
int main() {
freopen("maze.in", "r", stdin);
freopen("maze.out", "w", stdout);
n = read(), m = read();
for (int i = 1; i <= n; ++ i) {
int u_ = read(), v_ = read(), w_ = read();
AddEdge(u_, v_, w_);
AddEdge(v_, u_, w_);
AddEdge(u_, v_, 3 * w_);
AddEdge(v_, u_, 3 * w_);
}
Spfa();
printf("%d\n", secdis[n]);
return 0;
}
正解
#include <iostream>
#include <cstring>
using namespace std;
struct dd
{
int x,y,z;
};
int n,r,head[5001],tail[5001],dis[5001],s1,s2,ans=1000000000;
dd a[200001];
bool b[5001];
int read()
{
int n = 0, f = 1;
char c = getchar();
for(; isdigit(c); c = getchar())
{
n = n * 10 + c - '0';
}
return n * f;
}
void qsort(int l1,int r1)
{
if (l1>=r1) return;
int i=l1,j=r1,t=(i+j)/2;
dd k=a[t]; a[t]=a[i];
while (i<j)
{
while (a[j].x>=k.x&&i<j) --j;
if (i<j)
{
a[i]=a[j];
++i;
}
while (a[i].x<k.x&&i<j) ++i;
if (i<j)
{
a[j]=a[i];
--j;
}
}
a[i]=k;
qsort(l1,i-1); qsort(i+1,r1);
return;
}
void dfs(int k,int s3)
{
if (s3>s1||s3>=ans) return;
if (k==n&&s3>s2&&ans>s3)
{
ans=s3;
return;
}
for (int i=head[k]; i<=tail[k]; ++i)
dfs(a[i].y,s3+a[i].z);
return;
}
int main()
{
freopen("maze.in", "r", stdin);
freopen("maze.out", "w", stdout);
n = read(), r = read();
for (int i=1; i<=r; ++i)
{
a[2*i-1].x = read();a[2*i-1].y = read();a[2*i-1].z = read();
a[2*i].x=a[2*i-1].y; a[2*i].y=a[2*i-1].x;
a[2*i].z=a[2*i-1].z;
}
r*=2;
qsort(1,r);
memset(head,0,sizeof(head));
memset(tail,0,sizeof(tail));
head[1]=1; tail[n]=r;
for (int i=1; i<=r; ++i)
{
if (i>1&&a[i].x!=a[i-1].x)
head[a[i].x]=i;
if (i<r&&a[i].x!=a[i+1].x)
tail[a[i].x]=i;
}
for (int i=1; i<=n; ++i)
dis[i]=1000000000;
dis[1]=0;
memset(b,false,sizeof(b));
for (int i=1; i<=n; ++i)
{
int minn=1000000000,k=0;
for (int j=1; j<=n; ++j)
if (dis[j]<minn&&!b[j])
{
minn=dis[j];
k=j;
}
b[k]=true;
for (int j=head[k]; j<=tail[k]; ++j)
if (dis[a[j].y]>dis[k]+a[j].z)
{
dis[a[j].y]=dis[k]+a[j].z;
if (a[j].y==n) s1=a[j].z*2;
}
}
s2=dis[n]; s1+=s2;
dfs(1,0);
cout << ans << "\n";
return 0;
}