Codeforces Round #776 (Div. 3)
Codeforces Round #776 (Div. 3)
A - Deletions of Two Adjacent Letters
题意
\(T\)组数据,给出一个字符串 \(s\) ,且 \(|s|\) 为奇数,每次可以删除字符串中相邻的两个字母。询问是否可以通过若干次操作,让$s $的值等于字符 \(c\),如果可以输出YES
,否则输出NO
。
数据范围:\(1 \leq |s| \leq 49 ,1 \leq T \leq 10^3\)。
题解
我们考虑每一个 \(s_i = c\) 的位置 \(i\),如果该位置之前的字符可以通过若干次操作删成空,后面的字符也可以通过若干次操作删成空,那么该位置就是合法的。
否则,若最终保留\(s_i\),那么若干次不删除该字符的操作后,会形成三个字符组成的字符串,显然不可行。
时间复杂度\(O(\sum |s|)\)
C++ 代码实现
# pragma GCC optimize("Ofast", "inline", "-ffast-math")
# pragma GCC target("avx,sse2,sse3,sse4,mmx")
# include <bits/stdc++.h>
# define YON(x); puts((x)?"Yes":"No");
#define error(args...) { string _s = #args; replace(_s.begin(), _s.end(), ',', ' '); stringstream _ss(_s); istream_iterator<string> _it(_ss); err(_it, args); }
# define makep make_pair
# define maket make_tuple
# define eb emplace_back
# define pb push_back
# define fir first
# define sec second
// # define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
void err(istream_iterator<string> it) {}
template<typename T, typename... Args>
void err(istream_iterator<string> it, T a, Args... args) {
cerr << *it << " = " << a << endl;
err(++it, args...);
}
inline int read() {
int X=0,w=0;char c=0;
while (!(c>='0'&&c<='9')) w|=c=='-',c=getchar();
while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar();
return w?-X:X;
}
void solve() {
string s; cin>>s;
char t; cin>>t;
bool flag = false;
for (int i=0;i<s.length();i++) if (s[i] == t) {
if (i%2 == 0 && (s.length()-(i+1))%2 == 0) flag = true;
}
YON(flag);
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int tmp; cin>>tmp;
while (tmp--) {
solve();
}
return 0;
}
B - DIV + MOD
题意
\(t\) 组数据,每组数据给出 \(l,r,a\) :
求函数 \(f_a(x) = \lfloor\frac{x}{a}\rfloor + x \text{ mod } a\),在\(x \in [l,r]\) 的最大值。
数据满足:\(1 \leq l_i\leq r_i \leq 10^9 , 1 \leq t \leq 10^4 , 1 \leq a_i \leq 10^9\) 。
题解
考虑 \(x \text{ mod } a\) 的可能值为\([0,a)\),由容斥原理可知:
任何区间长度大于等于 \(a\) 的区间,都存在至少一个数 \(x\) ,让 \(x \text{ mod } a = a-1\)。
因此,对于这些区间,\(x\) 的取值等于最大的,除以 \(a\) 余 \(a-1\),那个数,可以让函数前半部分最大。
否则,对于区间长度小于 \(a\) ,函数必然在区间端点取到最值。
时间复杂度\(O(t)\)
# pragma GCC optimize("Ofast", "inline", "-ffast-math")
# pragma GCC target("avx,sse2,sse3,sse4,mmx")
# include <bits/stdc++.h>
# define YON(x); puts((x)?"Yes":"No");
#define error(args...) { string _s = #args; replace(_s.begin(), _s.end(), ',', ' '); stringstream _ss(_s); istream_iterator<string> _it(_ss); err(_it, args); }
# define makep make_pair
# define maket make_tuple
# define eb emplace_back
# define pb push_back
# define fir first
# define right rrr
# define sec second
// # define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
void err(istream_iterator<string> it) {}
template<typename T, typename... Args>
void err(istream_iterator<string> it, T a, Args... args) {
cerr << *it << " = " << a << endl;
err(++it, args...);
}
inline int read() {
int X=0,w=0;char c=0;
while (!(c>='0'&&c<='9')) w|=c=='-',c=getchar();
while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar();
return w?-X:X;
}
int f(int x,int a) {
return x/a+x%a;
}
void solve() {
int l,r,a; cin>>l>>r>>a;
int left = ((l+1)%a==0)?((l+1)/a):((l+1)/a+1);
int right = (r+1)/a;
if (left > right) {
printf("%d\n",max(f(l,a),f(r,a)));
} else printf("%d\n",f(right*a-1,a));
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int tmp; cin>>tmp;
while (tmp--) {
solve();
}
return 0;
}
C - Weight of the System of Nested Segments
题意
数轴上有 \(m\) 个坐标互不相同的点,第 \(i(1\leq i \leq m)\) 个点位置为 \(pos_i\),权值为 \(val_i\)。你需要从中选取 \(n\) 对点,两两组成一个区间,并让这些区间互相嵌套。
求出一种选取点的方案,让选取点的权值和尽量小。
数据范围满足:\(1 \leq 2n \leq m \leq 2\times 10^5\)。
题解
由于数轴上的点坐标两两不同,那么我们先选择权值最小的 \(2n\) 个点,可以保证选出的点权值和最小。
然后再对这些点配对组成嵌套区间,只需要对这些点对坐标排序即可。
时间复杂度 \(O(m \log_2 m )\)
C++ 代码示例
# pragma GCC optimize("Ofast", "inline", "-ffast-math")
# pragma GCC target("avx,sse2,sse3,sse4,mmx")
# include <bits/stdc++.h>
# define YON(x); puts((x)?"Yes":"No");
#define error(args...) { string _s = #args; replace(_s.begin(), _s.end(), ',', ' '); stringstream _ss(_s); istream_iterator<string> _it(_ss); err(_it, args); }
# define makep make_pair
# define maket make_tuple
# define eb emplace_back
# define pb push_back
# define fir first
# define sec second
// # define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 2e5 + 10;
void err(istream_iterator<string> it) {}
template<typename T, typename... Args>
void err(istream_iterator<string> it, T a, Args... args) {
cerr << *it << " = " << a << endl;
err(++it, args...);
}
inline int read() {
int X=0,w=0;char c=0;
while (!(c>='0'&&c<='9')) w|=c=='-',c=getchar();
while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar();
return w?-X:X;
}
struct rec{
int id,pos,val;
}a[N];
bool cmp1(rec a,rec b) {
return a.val < b.val;
}
bool cmp2(rec a,rec b) {
return a.pos<b.pos;
}
void solve() {
int n,m; cin>>n>>m;
for (int i=1;i<=m;i++) {
cin>>a[i].pos>>a[i].val;
a[i].id = i;
}
sort(a+1,a+1+m,cmp1);
int ans = 0;
for (int i=1;i<=2*n;i++) ans+=a[i].val;
cout<<ans<<endl;
sort(a+1,a+1+2*n,cmp2);
for (int i=1;i<=n;i++) {
cout<<a[i].id<<" "<<a[2*n-i+1].id<<endl;
}
}
int main() {
// std::ios::sync_with_stdio(false);
// cin.tie(0), cout.tie(0);
int tmp; cin>>tmp;
while (tmp--) {
solve();
}
return 0;
}
D - Twist the Permutation
题意
\(n\) 个数字 \(1-n\) 组成的排列 \(a\),初始满足\(a_i = i\),通过 $ n $ 次操作,第 \(i(1\leq i \leq n)\) 次操作可以做 \(c_i\) 次 关于\(a_1,...,a_i\) 的循环位移。最后生成数组 \(b_i\)。现在给出 \(b_i\) ,要求输出 \(c_i\)。
数据满足:\(1 \leq n \leq 2\times 10^3\)。
题解
从后到前考虑,每次通过若干次循环位移让 \(i\) 这个数字循环位移到 \(i\)这个位置即可。
直接模拟,复杂度为 \(O(n^2)\)
C++ 代码示例
# pragma GCC optimize("Ofast", "inline", "-ffast-math")
# pragma GCC target("avx,sse2,sse3,sse4,mmx")
# include <bits/stdc++.h>
# define YON(x); puts((x)?"Yes":"No");
#define error(args...) { string _s = #args; replace(_s.begin(), _s.end(), ',', ' '); stringstream _ss(_s); istream_iterator<string> _it(_ss); err(_it, args); }
# define makep make_pair
# define maket make_tuple
# define eb emplace_back
# define pb push_back
# define fir first
# define sec second
// # define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
void err(istream_iterator<string> it) {}
template<typename T, typename... Args>
void err(istream_iterator<string> it, T a, Args... args) {
cerr << *it << " = " << a << endl;
err(++it, args...);
}
inline int read() {
int X=0,w=0;char c=0;
while (!(c>='0'&&c<='9')) w|=c=='-',c=getchar();
while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar();
return w?-X:X;
}
int a[N],b[N],ans[N];
void solve() {
int n; cin>>n;
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=n;i>=1;i--) {
int pos;
for (int j=1;j<=n;j++) if (a[j] == i) {
pos = j; break;
}
if (pos == i) { ans[i] = 0; continue;}
ans[i] = pos;
for (int j=pos+1;j<=i;j++) b[j-pos]=a[j];
for (int j=1;j<=pos;j++) b[j+i-pos]=a[j];
for (int j=1;j<=i;j++) a[j]=b[j];
}
bool flag = true;
for (int i=1;i<=n;i++) if (a[i]!=i) {
flag = false; break;
}
if (!flag) {
puts("-1"); return;
}
for (int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int tmp; cin>>tmp;
while (tmp--) {
solve();
}
return 0;
}
E - Rescheduling the Exam
题意
\(n\) 次考试,共有 \(d\) 天,每次考试安排在第 \(a_i (1\leq a_i \leq d)\) 天,可以改动一次考试的时间,让相邻考试间隔最小值最大。
数据范围满足 \(1 \leq n \leq 2\times 10^5 , 1 \leq d \leq 10^9\) 。
题解
可以考虑二分答案,设当前需要check的答案为 \(S\),考虑判定。
用multiset
存储相邻两天之间的空窗间隔,枚举删除每一次考试。
删除后,multiset
中也需要对应进行修改,使得其中的数据符合将 \(a_i\) 不安排考试的情况。
接下来考虑插入一个新的考试,间隔最小值不超过 \(S\)。
- 插入在学期的最后一天
- 插入在两次考试之间
这样,可以完成判断。
这样总时间复杂度为\(O(n {\log_2}^2 n)\) 不能接受。
我们发现,这个二分实际上是可以省去的,只需要计算每次删除一个考试,插入一个考试的答案的最大值即可。
这样时间复杂度为\(O(n log_2 n)\),可以通过。
# pragma GCC optimize("Ofast", "inline", "-ffast-math")
# pragma GCC target("avx,sse2,sse3,sse4,mmx")
# include <bits/stdc++.h>
# define YON(x); puts((x)?"Yes":"No");
#define error(args...) { string _s = #args; replace(_s.begin(), _s.end(), ',', ' '); stringstream _ss(_s); istream_iterator<string> _it(_ss); err(_it, args); }
# define makep make_pair
# define maket make_tuple
# define eb emplace_back
# define pb push_back
# define fir first
# define sec second
// # define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 2e5 + 10;
void err(istream_iterator<string> it) {}
template<typename T, typename... Args>
void err(istream_iterator<string> it, T a, Args... args) {
cerr << *it << " = " << a << endl;
err(++it, args...);
}
inline int read() {
int X=0,w=0;char c=0;
while (!(c>='0'&&c<='9')) w|=c=='-',c=getchar();
while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar();
return w?-X:X;
}
int n,d;
int a[N];
void solve() {
n=read();d=read();
for (int i=1;i<=n;i++) a[i]=read();
int mx = 0;
sort(a+1,a+1+n);
multiset<int>st;
for (int i=1;i<=n;i++) st.insert(a[i]-a[i-1]-1);
int ans = 0;
for (int i=1;i<=n;i++) {
st.erase(st.find(a[i]-a[i-1]-1));
int bk = d-a[n]-1;
if (i!=n) {
st.erase(st.find(a[i+1]-a[i]-1));
st.insert(a[i+1]-a[i-1]-1);
} else bk = d-a[n-1]-1;
int res = *st.begin();
int mx = *(--st.end());
res = min(res,max(bk,(mx-1)/2));
ans = max(ans,res);
if (i!=n) {
st.erase(st.find(a[i+1]-a[i-1]-1));
st.insert(a[i+1]-a[i]-1);
}
st.insert(a[i]-a[i-1]-1);
}
printf("%d\n",ans);
}
int main() {
int tmp = read();
while (tmp--) {
solve();
}
return 0;
}
F - Vitaly and Advanced Useless Algorithms
题意
有 \(n\) 项作业,每项作业离ddl还有 \(a_i\) 个小时,有 \(m\) 次操作\(\{e_i,t_i,p_i\}\),每次操作可以花费\(t_i\)个小时,将作业\(e_i\)推进百分之\(t_i\)。输出可以完成作业的一种方法,或者输出不能完成作业。
数据满足\(1 \leq n,m \leq 10^5 , 1 \leq a_i \leq 10^9\)。
题解
对每一项作业进行 \(dp\) 计算完成该作业最小需要的时间,并记录方案。
这实际上是背包问题,可以从前往后更新,\(f[i][j]\)表示前\(i\)个操作,完成百分比为\(j\)的最小时间。
然后,贪心地从前往后完成每个作业,如果出现无法完成,则输出\(-1\)。
这样做时间复杂度为\(O(100n)\)。
# include <bits/stdc++.h>
# define time TIME
using namespace std;
const int N=1e5+10;
const int inf = 1e9+7;
int a[N];
struct qwq{ int t,p,id;};
vector<qwq>v[N];
int f[N][105];
vector<int>ans[N];
int time[N];
struct rec{
int i,j,op;
}g[N][105];
int main() {
// freopen("a.in","r",stdin);
int t; cin>>t;
while (t--) {
int n,m; cin>>n>>m;
for (int i=1;i<=n;i++) {
cin>>a[i];
v[i].clear();
time[i]=-1;
}
for (int i=1;i<=m;i++) {
int c,t,p; cin>>c>>t>>p;
v[c].push_back({t,p,i});
}
bool flag = true;
for (int c=1;c<=n;c++) {
if (v[c].size() == 0) {
puts("-1"); flag = false; break;
}
for (int i=0;i<=v[c].size();i++)
for (int j=0;j<=100;j++)
f[i][j] = inf, g[i][j].i=g[i][j].j=g[i][j].op=0;
f[0][0] = 0;
for (int i=0;i<v[c].size();i++)
for (int j=0;j<=100;j++) {
if (f[i+1][j] > f[i][j]) {
f[i+1][j]=f[i][j];
g[i+1][j] = (rec){i,j,-1};
}
if (f[i+1][min(j+v[c][i].p,100)] > f[i][j]+v[c][i].t) {
f[i+1][min(j+v[c][i].p,100)] = f[i][j]+v[c][i].t;
g[i+1][min(j+v[c][i].p,100)] = (rec){i,j,v[c][i].id};
}
}
if (f[v[c].size()][100]>=inf) {
puts("-1");
flag = false;
break;
}
int t = v[c].size(), p = 100;
ans[c].clear();
while (p>0) {
int id = g[t][p].op;
if (id != -1) ans[c].push_back(id);
int tmp1 = g[t][p].i,tmp2 = g[t][p].j;
t = tmp1; p = tmp2;
}
time[c] = f[v[c].size()][100];
}
if (!flag) {
continue;
}
int res = 0;
for (int i=1;i<=n;i++) {
if (res+time[i]>a[i]) {
puts("-1"); flag = false; break;
}
res+=time[i];
}
if (!flag) {
continue;
}
int sum = 0;
for (int i=1;i<=n;i++) sum+=ans[i].size();
printf("%d\n",sum);
for (int i=1;i<=n;i++)
for (int x:ans[i]) printf("%d ",x);
puts("");
}
return 0;
}
G - Counting Shortcuts
题意
\(n\) 个点 \(m\) 条边,边权都是 \(1\) 的有向图,求出最短路和次短路条数的和。
数据满足\(1 \leq n,m \leq 2\times 10^5\)。
题解
dijkstra
记录最短路次短路值及其次数,即可。
是个模板题,时间复杂度\(O((n+m)\log_2 n)\)
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
const int maxd=4e5+10,maxb=4e5+10;
const int mo = 1e9+7;
int head[maxd],tot=1;
struct asd{
int from,to,next,val;
}b[maxb<<1];
int n,m;
void ad(int aa,int bb,int cc){
b[tot].from=aa;
b[tot].to=bb;
b[tot].val=cc;
b[tot].next=head[aa];
head[aa]=tot++;
}
struct jie{
int num,dis,jud;
jie(int aa=0,int bb=0,int cc=0){
num=aa,dis=bb,jud=cc;
}
bool operator < (const jie& A) const{
return dis>A.dis;
}
};
priority_queue<jie> q;
int dis[maxd][3],cnt[maxd][3];
bool vis[maxd][3];
void dij(int xx){
for (int i=1;i<=n;i++) {
memset(dis[i],0x3f,sizeof(dis[i]));
memset(cnt[i],0,sizeof(cnt[i]));
memset(vis[i],0,sizeof(vis[i]));
}
dis[xx][0]=0,cnt[xx][0]=1;
q.push(jie(xx,0,0));
while(!q.empty()){
int now=q.top().num;
int judd=q.top().jud;
q.pop();
if(vis[now][judd]) continue;
vis[now][judd]=1;
for(int i=head[now];i!=-1;i=b[i].next){
int u=b[i].to;
int ndis=dis[now][judd]+b[i].val;
if(ndis<dis[u][0]){
if(dis[u][0]!=0x3f3f3f3f){
dis[u][1]=dis[u][0];
cnt[u][1]=cnt[u][0];
q.push(jie(u,dis[u][0],1));
}
dis[u][0]=ndis;
cnt[u][0]=cnt[now][judd];
q.push(jie(u,ndis,0));
}
else if(ndis==dis[u][0]){
(cnt[u][0]+=cnt[now][judd])%=mo;
}
else if(ndis==dis[u][1]){
(cnt[u][1]+=cnt[now][judd])%=mo;
}
else if(ndis<dis[u][1]){
dis[u][1]=ndis;
cnt[u][1]=cnt[now][judd];
q.push(jie(u,ndis,1));
}
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
tot=1;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) head[i]=-1;
int qd,zd;
scanf("%d%d",&qd,&zd);
for(int i=1;i<=m;i++){
int aa,bb,cc=1;
scanf("%d%d",&aa,&bb);
ad(aa,bb,cc);
ad(bb,aa,cc);
}
dij(qd);
int ans=cnt[zd][0];
if(dis[zd][0]==dis[zd][1]-1) (ans+=cnt[zd][1])%=mo;
printf("%d\n",ans);
}
return 0;
}