CF1941 div3题解
暑假集训个人赛打破防了,怒刷div3找自信
E题一眼单调队列,但是因为没看完题 a i , j + 1 a_{i,j}+1 ai,j+1样例不对,莫名开始怀疑自己的dp推错了,唐完了
A
O ( n m ) O(nm) O(nm)枚举,没什么好说的
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
void work()
{
int n,m,k;
cin>>n>>m>>k;
vi b(n),c(m);
rep(i,0,n) cin>>b[i];
rep(i,0,m) cin>>c[i];
int ans=0;
rep(i,0,n)
rep(j,0,m)
if(b[i]+c[j]<=k) ans++;
cout<<ans<<'\n';
}
int main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
cin>>t;
while(t--)
work();
return 0;
}
B
从左到右模拟一遍即可,但是我忘记判断倒数第二位也要为WA了一发
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
void work()
{
int n;
cin>>n;
vi a(n);
rep(i,0,n)
cin>>a[i];
rep(i,1,n-1)
{
if(a[i]<a[i-1]*2||a[i+1]<a[i-1]) {cout<<"NO\n";return;}
else
{
a[i]-=a[i-1]*2;
a[i+1]-=a[i-1];
}
}
if(a[n-1]==0&&a[n-2]==0) cout<<"YES\n";
else cout<<"NO\n";
}
int main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
cin>>t;
while(t--)
work();
return 0;
}
C
又是没读完题,一开始以为这里substring的定义是不连续的,想了半天
In formal language theory and computer science, a substring is a contiguous sequence of characters within a string
那么显然对于map和pie去掉中间的字母一定比去掉两边更好,特判一下mapie这种情况就可以了
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
void work()
{
int n;
cin>>n;
string s;
vector<bool> vis(n,false);
cin>>s;
int ans=0;
rep(i,0,n)
{
if(s[i]=='m')
{
if(i+4<n)
{
if(s[i+1]=='a'&&s[i+2]=='p'&&s[i+3]=='i'&&s[i+4]=='e') {ans++,vis[i+2]=true;continue;}
}
if(i+2<n)
if(!vis[i+2]&&s[i+1]=='a'&&s[i+2]=='p') ans++;
}
if(s[i]=='p'&&!vis[i])
{
if(i+2<n)
if(s[i+1]=='i'&&s[i+2]=='e') ans++;
}
}
cout<<ans<<'\n';
}
int main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
cin>>t;
while(t--)
work();
return 0;
}
D
n n n个人站一圈传球,传 m m m轮,告诉你每一轮的传球的距离和可能的方向,顺时针或逆时针或不确定,输出最后可能被传到的人的编号
1 ≤ n ≤ 1000 , 1 ≤ m ≤ 1000 1 \le n \le 1000,1 \le m \le 1000 1≤n≤1000,1≤m≤1000
第一分钟想到是一颗二叉树,又一想发现会重复,因为至多只有 1000 1000 1000个人,那么我们直接开两个set模拟一下就可以
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
void work()
{
int n,m,x;
cin>>n>>m>>x;
vector<int> di(m+1),op(m+1);
rep(i,1,m)
{
char ch;
cin>>di[i]>>ch;
if(ch=='?') op[i]=2;
if(ch=='0') op[i]=0;
if(ch=='1') op[i]=1;
}
set<int> S,nS;
S.insert(x-1);
rep(i,1,m)
{
trav(j,S)
{
if(op[i]==0)
{
nS.insert((j+di[i])%n);
}
if(op[i]==1)
{
nS.insert((j-di[i]+n)%n);
}
if(op[i]==2)
{
nS.insert((j+di[i])%n);
nS.insert((j-di[i]+n)%n);
}
}
S=nS;
nS.clear();
}
cout<<sz(S)<<'\n';
trav(i,S)
cout<<i+1<<' ';
cout<<'\n';
}
int main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
cin>>t;
while(t--)
work();
return 0;
}
E
给一个 n ∗ m n*m n∗m的矩阵 a i , j a_{i,j} ai,j表示水深,建造一座桥的要求如下
1.在 a i , j a_{i,j} ai,j格子下造桥墩的代价是 a i , j + 1 a_{i,j}+1 ai,j+1
2.桥是从 ( i , 1 ) (i,1) (i,1)到 ( i , m ) (i,m) (i,m)的,一座桥的所有桥墩之间的距离不超过 d d d,起点和终点一定要建造桥墩
问在连续 k k k行造 k k k个桥的最小代价是多少,不同桥的桥墩之间互不影响
写这个简单优化dp写了1个小时,耻辱,回忆心路历程
每一行之间相互独立,那我们单独考虑每一行
1、看完题想到设 d p [ i ] dp[i] dp[i]为在第 i i i个格子放桥墩,且前 i i i个格子稳定的最小代价,那么显然就是
d p [ i ] = m i n ( d p [ j ] ) + a [ i ] , ( i − j − 1 ≤ d 且 1 ≤ j ) dp[i]=min(dp[j])+a[i],(i-j-1 \le d且1 \le j) dp[i]=min(dp[j])+a[i],(i−j−1≤d且1≤j)
非常经典的可以用数据结构优化的 d p dp dp方程,可以单调队列也可以线段树也可以用堆(用之前一直弹出过期的堆头就可以)
2、直接写单调队列,敲完之后一看,样例每个点的答案都不对。然后就想
欸,我是不是没考虑前面不放桥墩的情况啊
然后就改成了 d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1],推了个一眼觉得很正确的式子开始重新写,发现答案又不对。
发现……放桥墩的情况你不能从不放桥墩转移过来,那这个式子不就跟一开始推得一样?
3、找了半天,最终决定再看一遍题,好家伙, a i , j + 1 a_{i,j}+1 ai,j+1,加上1之后也是飞快地过了
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
const ll inf=0x3f3f3f3f3f3f3f3f;
void work()
{
int n,m,k,d;
cin>>n>>m>>k>>d;
vector<ll> dp(m+1);
vector<ll> sum(n+1,0);
vector<vector<int> > a(n+1,vector<int> (m+1,0));
rep(i,1,n)
rep(j,1,m)
cin>>a[i][j];
sum[0]=0;
rep(i,1,n)
{
rep(j,1,m) dp[j]=inf;
dp[1]=1;
deque<int> q;
q.push_back(1);
rep(j,2,m)
{
/*
rep(k,0,1)
{while(!q[k].empty()&&j-q[k].front()-1>d) q[k].pop_front();}
dp[j][0]=dp[q[1].front()][1];
dp[j][1]=min(dp[q[0].front()][0],dp[q[1].front()][1])+a[i][j];
printf("q[0].front=%d,q[1].front=%d,dp[%d][0]=%lld,dp[%d][1]=%lld\n"
,q[0].front(),q[1].front(),j,dp[j][0],j,dp[j][1]);
rep(k,0,1)
{
while(!q[k].empty()&&dp[j][k]<dp[q[k].back()][k]) q[k].pop_back();
q[k].push_back(j);
}
*/
while(!q.empty()&&j-q.front()-1>d) q.pop_front();
dp[j]=dp[q.front()]+a[i][j]+1;
//printf("q.front=%d,dp[%d]=%lld\n",q.front(),j,dp[j]);
while(!q.empty()&&dp[j]<dp[q.back()]) q.pop_back();
q.push_back(j);
}
//cout<<'\n';
sum[i]=sum[i-1]+dp[m];
}
ll ans=inf;
rep(i,1,n)
{
if(i+k-1<=n) ans=min(ans,sum[i+k-1]-sum[i-1]);
}
cout<<ans<<'\n';
}
int main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
cin>>t;
while(t--)
work();
return 0;
}
F
给定数组 a , d , f a,d,f a,d,f,长度分别为 n , m , k n,m,k n,m,k, a a a有序,最多向 a a a中插入一个数 d i + f j d_i+f_j di+fj,且 a a a仍然有序,问操作后最大的 a i + 1 − a i a_{i+1}-a_i ai+1−ai为多少
$1 \le n,m,k \le 2e5 $
写这题的时候也很唐,还是太菜了
显然我们只需要考虑初始最大值和次大值,如果有多个最大值显然不可能改变答案
然后考虑在最大值之间插入一个值,我一开始的想法是 a p a_p ap和 a p + 1 a_{p+1} ap+1(设他们两个之间是最大值),中间有一个区间是可以使得答案更小的
但是这样也是 O ( m a x ( a i ) m ) O(max(a_i)m) O(max(ai)m)的
过了十多分钟才想起来,肯定越靠近中点的越优啊,那么二分就完事了
一个div3能有多难呢
然后就是学到了强制类型转换不能写在括号外面,WA了一发是因为
(ll)(a[mi+1]+a[mi])/2
写在这里会先加,已经爆成负数了再转成long long,应该写在加数前面先转一个变成long long
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
void work()
{
int n,m,k;
cin>>n>>m>>k;
vi a(n),d(m),f(k);
rep(i,0,n) cin>>a[i];
rep(i,0,m) cin>>d[i];
rep(i,0,k) cin>>f[i];
sort(f.begin(),f.end());
sort(d.begin(),d.end());
int mx=-1,mi=-1,mx1=-1;
rep(i,0,n-1)
{
if(a[i+1]-a[i]>mx)
{
mx1=mx;
mx=a[i+1]-a[i],mi=i;
}
else
{
if(a[i+1]-a[i]>mx1) mx1=a[i+1]-a[i];
}
}
rep(i,0,n-1)
if(mx==a[i+1]-a[i]&&mi!=i) {cout<<mx<<'\n';return;}
int ans=mx;
rep(i,0,m)
{
int l=0,r=k-1;
while(l<r)
{
int mid=l+r+1>>1;
if(d[i]+f[mid]<=((ll)a[mi+1]+a[mi])/2) l=mid;
else r=mid-1;
}
int res1=d[i]+f[l];
if(a[mi]<=res1&&res1<=a[mi+1])
ans=min(ans,max(res1-a[mi],a[mi+1]-res1));
if(l+1<k)
{
int res2=d[i]+f[l+1];
if(a[mi]<=res2&&res2<=a[mi+1])
ans=min(ans,max(res2-a[mi],a[mi+1]-res2));
}
}
cout<<max(ans,mx1)<<'\n';
}
int main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
cin>>t;
while(t--)
work();
return 0;
}
G
简单最短路,怎么建图一眼可以看出来
喜欢用vector但是开数组大小的时候开的 2 n 2n 2n没有开 n + m n+m n+m搞得WA了很多发还不明白
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
const int maxn=2e5+5;
const int inf=0x3f3f3f3f;
struct Edge
{
int to;int dis;
};
vector<Edge> adj[maxn*2];
vi dis;
map<int,int> f;
struct node
{
int u;int val;
};
bool operator<(const node &a,const node &b)
{
return a.val>b.val;
}
int be,ed;
int n,m,cnt;
void dijkstra()
{
rep(i,1,n+m) dis[i]=inf;
priority_queue<node> q;
q.push((node){be,0});dis[be]=0;
while(!q.empty())
{
node tmp=q.top();q.pop();
int u=tmp.u,d=tmp.val;
if(dis[u]!=d) continue;
trav(e,adj[u])
{
int v=e.to;
if(dis[u]+e.dis<dis[v])
{
dis[v]=dis[u]+e.dis;
q.push((node){v,dis[v]});
}
}
}
}
void init()
{
rep(i,1,n+m) adj[i].clear();
dis.clear();f.clear();
dis.resize(n+m+1);cnt=n;
}
void work()
{
cin>>n>>m;
init();
rep(i,1,m)
{
int x,y,z;
cin>>x>>y>>z;
if(!f[z]) f[z]=++cnt;
adj[x].push_back((Edge){f[z],0});
adj[y].push_back((Edge){f[z],0});
adj[f[z]].push_back((Edge){x,1});
adj[f[z]].push_back((Edge){y,1});
}
cin>>be>>ed;
dijkstra();
cout<<dis[ed]<<'\n';
}
int main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
cin>>t;
while(t--)
work();
return 0;
}