ABC429
恍惚间已经好久没写 ABC 了。
最好的一次,rk300,\(6\) 题 \(2\) 罚时。
A - Too Many Requests
模拟即可,时间复杂度为 \(O(n)\)。
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n,m;
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
if(i<=m)printf("OK\n");
else printf("Too Many Requests\n");
}
return 0;
}
B - N - 1
先求出所有数的和,然后枚举减去那个数,时间复杂度为 \(O(n)\)。
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n,m,a[1005],sum;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i],sum+=a[i];
for(int i=1;i<=n;i++){
if(sum-a[i]==m){
printf("Yes\n");
return 0;
}
}
printf("No\n");
return 0;
}
C - Odd One Subsequence
略有意思的一道题。
显然若两个三元组不同,则必须满足两个条件之一:
-
一个三元组中两个相等的元素和另一个三元组中两个相等的元素是不一样的。
-
一个三元组中两个相等的元素和另一个三元组中两个相等的元素一样,但第三个元素不一样。
这启发我们先枚举相等元素二元组,再随便找一个不相等的元素插进去,即可得到所有三元组。
假设 \(1\sim i-1\) 中有 \(c\) 个 \(a_j=a_i\),整个序列有 \(s\) 个 \(a_i\),则对答案的贡献为 \(c(n-s)\),枚举 \(i\) 用桶计算即可做到 \(O(n)\)。
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N=2e5+10;
typedef long long ll;
ll n,a[N],cnt[N],mp[N],ans;
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",a+i);
cnt[a[i]]++;
}
for(int i=1;i<=n;i++){
ans+=mp[a[i]]*(n-cnt[a[i]]);
mp[a[i]]++;
}
printf("%lld\n",ans);
return 0;
}
D - On AtCoder Conference
先离散化,求出每个位置上有多少人。然后短环成链,求前缀和。
若从 \(x+0.5\) 出发,第一个经过 \(i\),则最后会停到第一个满足 \(s_j-s_{i-1}\ge C\) 的 \(j\),可以二分查找求得,接下来只需要求出有多少个 \(x\) 第一次经过 \(i\) 即可,借助离散化数组,画图手摸即可实现,复杂度为 \(O(n\log n)\)。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
typedef long long ll;
ll n,m,c,x[N],s[N<<2],bk[N],ans;
int main(){
scanf("%lld %lld %lld",&n,&m,&c);
for(int i=1;i<=n;i++){
scanf("%lld",x+i);
bk[i]=x[i];
}
sort(bk+1,bk+1+n);
int tot=unique(bk+1,bk+1+n)-(bk+1);
for(int i=1;i<=n;i++){
x[i]=lower_bound(bk+1,bk+1+tot,x[i])-bk;
s[x[i]]++;
}
for(int i=1;i<=tot;i++)s[tot+i]=s[i];
for(int i=1;i<=tot*2;i++)s[i]+=s[i-1];
for(int i=1;i<=tot;i++){
int p=lower_bound(s+1,s+1+2*tot,s[i-1]+c)-s;
if(i==1)ans+=(m-bk[tot]+bk[i])*(s[p]-s[i-1]);
else ans+=(bk[i]-bk[i-1])*(s[p]-s[i-1]);
}
printf("%lld\n",ans);
return 0;
}
E - Hit and Away
我们考虑对于每一个危险点 \(x\),找到两个不同的距离 \(x\) 最近的安全点,他们到 \(x\) 的答案相加即为所求。
正着做有限制,考虑反过来,从每个安全点出发跑多源最短路来找两个不同的距离 \(x\) 最近的安全点。
图没有边权,可以直接 BFS,普通的 BFS 只要访问过一遍就不访问第二次。这里稍微改变规则,每个点可以被访问不超过两次,但保证第一次访问和第二次访问的来源起点不是同一个安全点。
直接把所有状态扔队列里跑就行了,复杂度为 \(O(n)\)。
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <map>
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
using namespace std;
const int N=2e5+10;
vector<int>G[N];
map<int,bool>vis[N];
int n,m,ans[N];
char s[N];
struct node{int x,f,d;};
queue<node>q;
void add(int x,int y){
G[x].push_back(y);
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1,u,v;i<=m;i++){
scanf("%d %d",&u,&v);
add(u,v),add(v,u);
}
scanf("%s",s+1);
for(int i=1;i<=n;i++)if(s[i]=='S')q.push(node{i,i,0});
while(q.size()){
node t=q.front();q.pop();
if(vis[t.x].size()>1||vis[t.x].count(t.f))continue;
vis[t.x][t.f]=1;
if(s[t.x]=='D')ans[t.x]+=t.d;
for(auto y:G[t.x]){
if(vis[y].size()>1||vis[y].count(t.f))continue;
q.push(node{y,t.f,t.d+1});
}
}
for(int i=1;i<=n;i++)if(s[i]=='D')printf("%d\n",ans[i]);
return 0;
}
F - Shortest Path Query
分类讨论太麻烦,直接暴力 ddp!
注意到我们只会从当前列走到下一列,考虑相邻两列直接的状态转移,设 \(f_{i,j}\) 表示从 \((1,1)\) 走到 \((j,i)\) 的最小步数。
我们假定对于任意位置 \((x,y)\),先从 \((x,y)\) 走到 \((x,y+1)\),然后再往上或下走,若要从 \((1,i-1)\) 走到 \((3,i)\),必须满足 \((1,i),(2,i),(3,i)\) 都是空地,则可以沿着 \((1,i-1)\to (1,i)\to (2,i)\to (3,i)\) 的顺序走,则用 \(f_{i-1,1}+3\) 更新 \(f_{i,3}\)。其他情况也是如此。
用矩阵表示这个转移过程,若不能转移则在对应位置设置为 \(+\infty\)。建出 \(n\) 列的转移矩阵,将一个 \([-1,+\infty,+\infty]\) 的初始向量依次乘上 \(1\sim n\) 的转移矩阵,最后取出终止向量的第三个元素,若距离超过 \(n\) 很多,则直接判定无解。
对于翻转操作,用线段树维护区间矩阵乘积,一次翻转就是一次单点改矩阵,复杂度 \(O(\log nV^3)\)。最后用初始向量和求出根节点的 \(1\sim n\) 矩阵之积乘在一起即可,总复杂度 \(O(n\log nV^3)\),其中 \(V=3\)。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
struct mat{
int n,m;
int x[3][3];
void init(int r,int c){
n=r,m=c;
memset(x,0x3f,sizeof(x));
}
}c,val[N<<2],ans;
mat operator * (const mat &a,const mat &b){
mat c;c.init(a.n,b.m);
for(int i=0;i<a.n;i++)
for(int j=0;j<b.m;j++)
for(int k=0;k<a.m;k++)
c.x[i][j]=min(c.x[i][j],a.x[i][k]+b.x[k][j]);
return c;
}
mat getb(char a,char b,char c){
mat p;p.init(3,3);
p.x[0][0]=((a=='#')?inf:1),p.x[0][1]=((a=='#'||b=='#')?inf:2),p.x[0][2]=((a=='#'||b=='#'||c=='#')?inf:3);
p.x[1][0]=((a=='#'||b=='#')?inf:2),p.x[1][1]=((b=='#')?inf:1),p.x[1][2]=((b=='#'||c=='#')?inf:2);
p.x[2][0]=((a=='#'||b=='#'||c=='#')?inf:3),p.x[2][1]=((b=='#'||c=='#')?inf:2),p.x[2][2]=((c=='#')?inf:1);
return p;
}
char s[3][N];
int n,q;
#define ls (p<<1)
#define rs (p<<1|1)
void pushup(int p){
val[p]=val[ls]*val[rs];
}
void build(int p,int l,int r){
if(l==r){
val[p]=getb(s[0][l],s[1][l],s[2][l]);
}else{
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
pushup(p);
}
return;
}
void upd(int p,int l,int r,int x){
if(l==r){
val[p]=getb(s[0][x],s[1][x],s[2][x]);
}else{
int mid=(l+r)>>1;
if(x<=mid)upd(ls,l,mid,x);
if(x>mid)upd(rs,mid+1,r,x);
pushup(p);
}
return;
}
int main(){
scanf("%d",&n);
for(int i=0;i<=2;i++){
scanf("%s",s[i]+1);
}
build(1,1,n);
scanf("%d",&q);
int x,y;
while(q--){
scanf("%d %d",&x,&y);x--;
if(s[x][y]=='.')s[x][y]='#';
else s[x][y]='.';
upd(1,1,n,y);
ans.init(1,3);ans.x[0][0]=-1;
ans=ans*val[1];
if(ans.x[0][2]>=5*n)printf("-1\n");
else printf("%d\n",ans.x[0][2]);
}
return 0;
}
好感动,第一次在正式比赛中写出正确的 ddp(虽然是板子中的板子)。
G - Sum of Pow of Mod of Linear
神秘数学题,我不会。
写完,收工!
魔法少女小圆,启动!


浙公网安备 33010602011771号