2024ICPC南京部分题解
Left Shifting 3
题面:
给定一个长度为 \(n\) 的字符串 \(S = s_0s_1 \cdots s_{n-1}\),你可以将 \(S\) 向左移动最多 \(k\) 次(包括零次)。计算在操作后字符串中包含的“nanjing”子字符串的最大数量。
更正式地说,让 \(f(S, d)\) 成为将 \(S\) 向左移动 \(d\) 次得到的字符串。也就是说,
让
让 \(h(d)\) 成为整数对 \((l, r)\) 的数量,使得 \(0 \leq l \leq r < n\) 并且 \(g(f(S, d), l, r) = \text{"nanjing"}\)。找到一个整数 \(d\),使得 \(0 \leq d \leq k\) 来最大化 \(h(d)\) 并输出这个最大化的值。
输入:
有多个测试用例。输入的第一行包含一个整数 \(T\),表示测试用例的数量。对于每个测试用例:
第一行包含两个整数 \(n\) 和 \(k\)(\(1 \leq n \leq 2 \times 10^5, 0 \leq k \leq 10^9\)),表示字符串的长度和你最多可以执行的左移次数。
第二行包含一个长度为 \(n\) 的字符串 \(s_0s_1 \cdots s_{n-1}\)。该字符串由小写英文字母组成。
保证所有测试用例的 \(n\) 之和不会超过 \(5 \times 10^5\)。
输出:
对于每个测试用例,输出一行包含一个整数,表示字符串中包含的“nanjing”子字符串的最大数量。
样例:
4
21 10
jingicpcnanjingsuanan
21 0
jingicpcnanjingsuanan
21 3
nanjingnanjingnanjing
4 100
icpc
——————
2
1
3
0
思路:不管k多大,循环位移最多也就min(n+n,n+m)次,于是考虑n倍增暴力,然后写个前缀和,对于超出n的位置选择pre[i]-pre[n-i]最大值即可,m+n之后不要遍历了
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll pre[650000];
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n,m;
cin>>n>>m;
string f;
cin>>f;
f+=f;
f='0'+f;
m=min(m,n);
ll ans=0;
//cout<<n+m<<endl;
for(ll i=1;i<=n+m;i++)
{
pre[i]=0;
pre[i]=max(pre[i],pre[i-1]);
if(i+7-1<=n+m)
{
string k=f.substr(i,7);
if(k=="nanjing")
{
pre[i]++;
}
//cout<<i
}
if(i>=n)
ans=max(ans,pre[i]-pre[i-n]);
}
cout<<ans<<endl;
}
}
J.Social Media
题面:
在一个社交媒体平台上,用户可以在其他人的帖子下留言来表达他们的想法。然而,这些评论并不是每个人都能看到的。具体来说,对于用户 $ C $ 要看到用户 $ A $ 在用户 $ B $ 的帖子下的评论,他/她必须同时与 $A $ 和 $ B $ 成为朋友。如果一个用户在他/她自己的帖子下留言,他/她所有的朋友都可以看到这条评论。
作为这个平台上的一个活跃用户,你想要看到尽可能多的评论。平台上有 $ k $个用户(不包括你),编号从 1 到 $ k $。平台上也有 $ m $ 条评论,但你可能无法看到所有评论,因为你只有 $n $ 个朋友。由于你需要参加2024年ICPC亚洲南京区域赛,你没有时间交太多新朋友。如果你在平台上最多交两个新朋友,你最多能看到多少条评论?
输入:
有多个测试用例。输入的第一行包含一个整数 \(T\),表示测试用例的数量。对于每个测试用例:
第一行包含三个整数 \(n, m, k \left(1 \leq n \leq k \leq 2 \times 10^{5}, 1 \leq m \leq 2 \times 10^{5}\right)\),表示你的朋友数量、评论数量和平台上的用户数量(不包括你自己)。
第二行包含 \(n\) 个不同的整数 \(f_1, f_2, \cdots, f_n \left(1 \leq f_i \leq k\right)\),表示你在平台上的朋友。
在接下来的 \(m\) 行中,第 \(i\) 行包含两个整数 \(a_i\) 和 \(b_i \left(1 \leq a_i, b_i \leq k\right)\),表示用户 \(a_i\) 在用户 \(b_i\) 的帖子下写的评论。
保证所有测试用例的 \(k\) 之和以及 \(m\) 之和都不会超过 \(2 \times 10^{5}\)。
输出:
对于每个测试用例,输出一行包含一个整数,表示如果你在平台上最多交两个新朋友,你可以看到的评论的最大数量。
样例:
5
4 12 7
5 7 3 6
3 6
2 2
1 4
2 4
1 3
7 6
4 1
5 4
1 1
1 1
2 1
3 7
2 7 6
2 4
1 2
3 2
2 5
5 4
2 6
4 6
2 6
1 1 2
1
1 2
2 1 2
1 2
1 2
2 1 100
24 11
11 24
——————
9
5
1
1
1
思路:对于两个要选择的朋友,无非就是给出的连边出现过的,要么就是都没出现的,
后续单纯说连是指连了现在朋友的无关系人员
如果连边出现过,就考虑选择连,连,不连,不连,连,不连,三种情况
如果不连,不连,直接map找到最大值
如果连,不连,则直接用标记找一个连了的,然后另一个没连过的,算他俩的连边和自连边和连现有朋友的最大值
如果连,连,考虑现有连边,自环连边,还有两者连边即可
如果连边没出现过,就只要选择连边数最大的两个与现有朋友连过且不是现有朋友的人员
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s
{
ll x,y;
}p[650000];
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n,m,k;
cin>>n>>m>>k;
map<ll,bool>q;
map<ll,ll>f1,f3,f4;
map<pair<ll,ll>,ll>o,op;
for(ll i=1;i<=n;i++)
{
ll x;
cin>>x;
q[x]=1;
}
ll ans=0;
ll cnt=0;
for(ll i=1;i<=m;i++)
{
ll x,y;
cin>>x>>y;
if(q[x]&&q[y])
{
ans++;
continue;
}
else
{
cnt++;
if(x>y)swap(x,y);
p[cnt].x=x,p[cnt].y=y;
if(q[x])f1[y]=1,f3[y]++;//标记有联系过朋友
if(q[y])f1[x]=1,f3[x]++;
if(x==y)
{
f4[x]++;
}
else
o[{x,y}]++;
}
}
ll an=0;
priority_queue<ll>cf;
map<ll,ll>jl;
for(ll i=1;i<=cnt;i++)
{
if(q[p[i].x]==0&&q[p[i].y]==0&&p[i].x!=p[i].y)
{
if(f1[p[i].x]&&f1[p[i].y]==0)
{
an=max(an,ans+f3[p[i].x]+f4[p[i].x]+o[{p[i].x,p[i].y}]+f4[p[i].y]);
}
else if(f1[p[i].x]==0&&f1[p[i].y])
{
an=max(an,ans+f3[p[i].y]+f4[p[i].x]+o[{p[i].x,p[i].y}]+f4[p[i].y]);
}
else if(f1[p[i].x]&&f1[p[i].y])
{
an=max(an,ans+f4[p[i].x]+f4[p[i].y]+f3[p[i].x]+f3[p[i].y]+o[{p[i].x,p[i].y}]);
}
else
{
an=max(an,o[{p[i].x,p[i].y}]+ans+f4[p[i].x]+f4[p[i].y]);
}
}
else
{
if(q[p[i].x]==0&&jl[p[i].x]==0)
{
cf.push(f3[p[i].x]+f4[p[i].x]);
jl[p[i].x]=1;
}
if(q[p[i].y]==0&&jl[p[i].y]==0)
{
cf.push(f3[p[i].y]+f4[p[i].y]);
jl[p[i].y]=1;
}
}
an=max(an,o[{p[i].x,p[i].y}]);
}
ll ko=0;
ll gs=2;
//cout<<f3[4]<<endl;
while(!cf.empty())
{
gs--;
ko+=cf.top();
cf.pop();
if(gs==0)
break;
}
an=max(an,ko+ans);
cout<<an<<endl;
}
}
K.Strips
题面:
有 $ w$ 个单元格排列成一行,从左到右编号为 1 到 $ w $。在这些单元格中,有 $n \(个是红色的,\) m $ 个是黑色的,剩下的 $ w-n-m $ 个单元格是白色的。
你需要用一些条带覆盖所有的红色单元格。每个条带必须覆盖 $ k $ 个连续的单元格。找到一种方法来覆盖所有红色单元格,同时满足以下所有约束条件:
- 每个红色单元格都被条带覆盖。
- 没有黑色单元格被条带覆盖。
- 没有两个条带覆盖同一个单元格。也就是说,每个单元格最多被一个条带覆盖。
- 使用的条带数量尽可能少。
输入:
有多个测试用例。输入的第一行包含一个整数 \(T\),表示测试用例的数量。对于每个测试用例:
第一行包含四个整数 \(n, m, k\) 和 \(w\)(\(1 \leq n, m \leq 10^5, 1 \leq k \leq w \leq 10^9, n + m \leq w\)),表示红色单元格的数量、黑色单元格的数量、每个条带的长度和总单元格数。
第二行包含 \(n\) 个整数 \(a_1, a_2, \cdots, a_n\)(\(1 \leq a_i \leq w\)),表示单元格 \(a_i\) 是红色的。
第三行包含 \(m\) 个整数 \(b_1, b_2, \cdots, b_m\)(\(1 \leq b_i \leq w\)),表示单元格 \(b_i\) 是黑色的。
保证给定的 \((n+m)\) 个单元格是不同的。还保证所有测试用例的 \(n\) 之和以及 \(m\) 之和都不会超过 \(2 \times 10^5\)。
输出:
对于每个测试用例:
如果可能在满足所有约束条件的情况下覆盖所有红色单元格,首先输出一行包含一个整数 \(c\),表示使用的条带数量最少。然后输出另一行包含 \(c\) 个整数 \(l_1, l_2, \cdots, l_c\)(\(1 \leq l_i \leq w - k + 1\)),用空格分隔,其中 \(l_i\) 是第 \(i\) 条带覆盖的最左边的单元格。如果有多个有效答案,你可以输出其中任何一个。
如果无法做到这一点,只需输出一行 -1。
样例:
4
5 2 3 16
7 11 2 9 14
13 5
3 2 4 11
6 10 2
1 11
2 1 2 6
1 5
3
2 1 2 6
1 5
2
——————
4
6 2 14 9
-1
2
1 4
-1
思路:当没有限制时,其实最优解就是能以某点作为最左端进行覆盖就是最好的,能覆盖就覆盖于是这题加了限制,就是在最优秀的情况下往左移动知道限制满足为止,
如果最后不满足,则返回-1.没有思路时,还是得从根本出发。
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll a[450000];
ll b[450000];
vector<ll> ans;
ll d[450000];
int main()
{
fio();
ll t;
cin >> t;
while (t--)
{
ans.clear();
ll n, m, k, w;
cin >> n >> m >> k >> w;
for (ll i = 1; i <= n; i++)
{
cin >> a[i];
}
for (ll i = 1; i <= m; i++)cin >> b[i];
b[m + 1] = w + 1;
a[n+1]=9999999999999999;
b[0] = 0;
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + m);
ll l,r;
ll cnt=1;
ll pd=0;
for(ll i=0;i<=m;i++)
{
l=r=cnt;
if(cnt==n+1)break;
if(b[i+1]<a[r])continue;
while(a[cnt]>=b[i]&&a[cnt]<=b[i+1])
{
r=cnt;
cnt++;
}
ll op=0;
for(ll j=l;j<=r;j++)
{
if(j==l)
{
op++;
d[op]=a[j];
}
else
{
if(a[j]<=d[op]+k-1)continue;
else
{
op++;
d[op]=a[j];
}
}
}
d[op+1]=b[i+1];
//cout<<b[i+1]<<endl;
for(ll j=op;j>=1;j--)
{
if(d[j]+k-1>=d[j+1])
{
ll u=d[j+1]-(k-1)-1;
d[j]=u;
}
else
continue;
}
//cout<<d[1]<<" "<<d[2]<<" "<<d[3]<<endl;
//cout<<cnt<<endl;
if(d[1]<=b[i])
{
pd=1;
break;
}
else
{
for(ll j=1;j<=op;j++)ans.push_back(d[j]);
}
}
if(pd)
cout<<-1<<endl;
else
{
cout<<ans.size()<<endl;
for(auto j:ans)
cout<<j<<" ";
cout<<endl;
}
}
}