【LGR-221-Div.3】洛谷基础赛 #21 赛后VP与题解
这场比赛没来得及打(逃)
然后趁有个时间VP了一下,感觉题目质量还可以
| 题目 | 个人难度评测 | 官方难度评测 |
|---|---|---|
| A踩点 | 红 | 红 |
| B共同兴趣 | 黄 | 橙 |
| C电 van | 黄 | 黄 |
| D跳舞机 | 绿 | 绿 |
单纯个人预测
然后我这场比赛VP顺序是 C -> D -> A -> B
讲真的B题我一开始觉得(500)^3怎么可能能过啊,然后用bitset优化过去了
Problem A: 踩点
一道签到题,只要每次都判断当前L[i] 和 R[i-1]的差值是否足够用于打篮球的时间
如果足够就ans += L[i] - R[i-1] - T;
反之就什么都不操作
Accepted代码:
//P12244
#include <iostream>
#define ll long long
using namespace std;
ll l[100010],r[100010];
int main()
{
ll ans = 0;
ll N,T;
cin>>N>>T;
for(int i=1; i<=N; i++)
{
cin>>l[i]>>r[i];
}
for(int i=2; i<=N; i++)
{
if(l[i] - r[i-1] > T)
ans += l[i] - r[i-1] - T;
}
cout<<ans;
return 0;
}
Problem B: 共同兴趣
这道题我用了bitset操作,从左到右遍历修改第一个学生所有的兴趣依次变成1然后统计一下与自己共同兴趣数最多的学生个数
需要用N^2预处理一下其他学生与学生之间的最多共同兴趣个数
当学生1与其他学生的最多兴趣个数甚至不如该学生本身与别人的最多同兴趣个数时,不累加。
简单来说就是N^2处理所有学生的兴趣and操作并返回有多少个1即可
Accepted代码:
//P12245
#include <iostream>
#include <bitset>
#define ll long long
using namespace std;
bitset<510> bs[100010];
bitset<510> BS;
bitset<510> Bstag;
ll F[510];
ll dp[510];
int main()
{
ll N,M,a;
cin>>N>>M;
for(int i=1; i<=M; i++)
{
cin>>F[i];
if(F[i])
bs[1].set(i,true);
}
for(int i=2; i<=N; i++)
{
for(int j=1; j<=M; j++)
{
cin>>a;
if(a)
bs[i].set(j,true);
}
}
for(int i=2; i<=N; i++)
{
ll Max = 0;
for(int j=2; j<=N; j++)
{
BS = bs[i] & bs[j];
//cout<<BS.count()<<endl;
ll Ftag = BS.count();
if(i != j)
Max = max(Max,Ftag);
}
dp[i] = Max;
}
ll Max = 0;
for(int r=1; r<=M; r++)
{
Bstag = bs[1];
Bstag.set(r,true);
ll ans = 0;
for(int i=2; i<=N; i++)
{
BS = bs[i] & Bstag;
//cout<<BS.count()<<endl;
ll Ftag = BS.count();
if(Ftag >= dp[i])
ans ++;
}
Max = max(Max,ans);
} cout<<Max;
return 0;
}

Problem C: 电 van
我靠这题怎么那么坏啊,,有时候考虑不周全直接Wa一大堆,
一开始是想到了N^2算法 但是觉得没什么必要,主要是你如果是相邻交换的话,还是有一定规律可寻找的
就好比如说:
对于vannnn
原本字符串中 van 作为子序列的出现次数为4
当你调换a与n的位置时 ans = 4-1
当你调换v与a的位置时 ans = 4-4
一共六种调换可能啦,稍微麻烦一点但是也很好实现
可以考虑先计算答案后再根据调换的可能改变ans
时间复杂度O(N)
Accepted代码:
//P12246
#include <iostream>
#include <map>
#define MaxN 1000010
#define ll long long
using namespace std;
map<char,ll> Map;
char ch[MaxN];
ll x,Sumv[MaxN],Cnt[MaxN],Suma,ans;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
ll N,M;
cin>>N>>M;
for(int i=1; i<=N; i++)
{
cin>>ch[i];
if(ch[i] == 'v') Sumv[i] = Sumv[i-1]+1;
else Sumv[i] = Sumv[i-1];
//统计v在各个i的个数
//因为可能存在swap(a,v)的情况从而改变ans数量
}
for(int i=N; i>0; i--)
{
Map[ch[i]] ++;
if(ch[i] == 'a')
{
Cnt[i] = Map['n'];
Suma += Cnt[i];
}
if(ch[i] == 'v')
{
ans += Suma;
}
}
for(int i=1; i<=M; i++)
{
cin>>x;
char L = ch[x];
char R = ch[x+1];
ll l, r;
if(L == 'v')
{
if(R == 'a')
{
ans -= Cnt[x+1];
l = Cnt[x];
r = Cnt[x+1];
Cnt[x] = r;
Cnt[x+1] = l;
}
if(R != L)
{
Sumv[x] --;
}
ch[x] = R;
ch[x+1] = L;
}
if(L == 'a')
{
if(R == 'n')
{
ans -= Sumv[x];
l = Cnt[x];
r = Cnt[x+1];
Cnt[x] = r;
Cnt[x+1] = l-1;
}
if(R == 'v')
{
Sumv[x] ++;
ans += Cnt[x];
}
ch[x] = R;
ch[x+1] = L;
if(R != 'n')
{
l = Cnt[x];
r = Cnt[x+1];
Cnt[x] = r;
Cnt[x+1] = l;
}
}
if(L == 'n')
{
if(R == 'a')
{
ans += Sumv[x];
l = Cnt[x];
r = Cnt[x+1];
Cnt[x] = r+1;
Cnt[x+1] = l;
}
if(R == 'v')
{
Sumv[x] ++;
}
ch[x] = R;
ch[x+1] = L;
}
cout<<ans<<endl;
}
return 0;
}

Problem D: 跳舞机
纯纯因为我脑子有问题,dp数组写错了调了一天
这个题目首先要先想到O(NM)的一个dp思路
就是[i,i+k-1] (k是时间间隔)可以从答案[i-k,i-1]推导的
附上一张图:

对的,所以每次的dp数组参考以前的选取我们会取得最优解
比如说样例:
2 5 3
1 3 5
3 5 8
我们当然是优先选择3 5 8让1 3处于没人玩的状态,故:
dp[i] = max(dp[i-1],dp[i-k] + w[i])
当然的,我的程序中 i 表示的是时间链中的右边界
(就是说如果k = 2,那么我的dp数组将会从2一直到M)
dp数组想到了 优化呢?O(NM)是必定TLE的,有没有更好的解法呢
我们知道 O(NM)的前提是你要遍历所有的N 但是完全没有这个必要
至少L[i] > i(i是指当前时间)的我们不需要去看,因为他都不在所以不用考虑
那我们考虑排序?
考虑用结构体+cmp对所有玩家的L[i]进行升序排序
但是只要数据强度足够,你还是会TLE
那怎么办 那当然是跑贪心!
跑贪心? 对! 我们当然期望能玩的人,能产生最大的w[i]
每次都在满足条件的人里面选一个max(w[i])?
优先队列,每次都看看队头是不是满足条件,满足条件就加一次w,不满足就出去!
同时要因为i的更新从而更新一下队列内的元素
时间复杂度O(mlogn)
Accepted代码:
//P12247
#include <iostream>
#include <algorithm>
#include <queue>
#define ll long long
using namespace std;
struct point{
ll L;
ll R;
ll w;
bool operator<(const point &x) const{
return x.w > w;
}
}p[500010];
bool cmp(point a, point b)
{
return a.L < b.L;
}
ll N,M,K;
ll dp[500010];
priority_queue<point> q;
int main()
{
cin>>N>>M>>K;
for(int i=1; i<=N; i++)
{
cin>>p[i].L>>p[i].R>>p[i].w;
}
sort(p+1,p+1+N,cmp);
ll L = 1;
for(int i=K; i<=M; i++)
{
ll Left = i - K + 1;
while(p[L].L <= Left && L <= N)
{
q.push(p[L]);
L ++;
}
ll W = 0;
if(!q.empty())
while(q.top().R < i)
{
q.pop();
if(q.empty()) break;
}
if(!q.empty())
W = q.top().w;
dp[i] = max(dp[i-1],dp[i-K] + W);
}
cout<<dp[M];
return 0;
}

浙公网安备 33010602011771号