OI 笑传 #23
今天是 ABC429 CDEF。被 E 卡到破防说是。代码能力场。
ABC429C
给三元组的样子分个类,\(AAB,ABB,ABA\) 这三种。
对于前两种,用个桶前缀后缀一下算贡献即可。
对于中间的,我们动态维护每种数左边和右边数量的乘积,因为每次只会改一个数,维护是简单的。
剩下就是代码了,一遍写对。
code
Show me the code
#define rd read()
#define mkp make_pair
#define ls p<<1
#define rs p<<1|1
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#include<bits/stdc++.h>
using namespace std;
typedef long long i64;
typedef unsigned long long u64;
typedef unsigned int u32;
typedef __int128 i128;
i64 read(){
i64 x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
const int N=2e5+5;
i64 a[N];
i64 t[N];
i64 t1[N];
i64 t2[N];
pair<i64,i64> t3[N];
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];t[a[i]]++;
}
i64 ans=0;
for(int i=1;i<=n;i++){
t1[a[i]]++;
if(t1[a[i]]>=2){
ans+=(t1[a[i]]-1)*(n-i-(t[a[i]]-t1[a[i]]));
}
t3[i].second=t[i];
}
for(int i=n;i>=1;i--){
t2[a[i]]++;
if(t2[a[i]]>=2){
ans+=(t2[a[i]]-1)*(i-1-(t[a[i]]-t2[a[i]]));
}
}
i64 msum=0;
for(int i=1;i<=n;i++){
msum-=t3[a[i]].first*t3[a[i]].second;
ans+=msum;
t3[a[i]].first++;
t3[a[i]].second--;
msum+=t3[a[i]].first*t3[a[i]].second;
}
cout<<ans;
return 0;
}
ABC429D
朴素二分题,首先离散化,把每个放了人的位置的答案计算出来即可。
注意环的处理和相邻人的位置贡献的计算。
赛时好像这题也卡飞不少人?
code
Show me the code
#define rd read()
#define mkp make_pair
#define ls p<<1
#define rs p<<1|1
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#include<bits/stdc++.h>
using namespace std;
typedef long long i64;
typedef unsigned long long u64;
typedef unsigned int u32;
typedef __int128 i128;
i64 read(){
i64 x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
const int N=6e5+5;
map<i64,int> mp;
int r[N];
i64 pre[N*2];
i64 po[N];
i64 ans[N];
i64 getsum(int len,int p){
return pre[p+len-1]-pre[p-1];
}
int main(){
i64 n,m,c;cin>>n>>m>>c;
for(int i=1;i<=n;i++){
i64 p;cin>>p;
mp[p]++;
}
int cnt=0;
for(pair<i64,int> v:mp){
cnt++;
r[cnt]=v.second;
po[cnt]=v.first;
pre[cnt]=pre[cnt-1]+r[cnt];
}
for(int i=cnt+1;i<=2*cnt;i++){
pre[i]=pre[i-1]+r[i-cnt];
}
for(int i=1;i<=cnt;i++){
int l=0,r=cnt+1;
while(l<r){
int mid=(l+r)>>1;
if(getsum(mid,i)<c)l=mid+1;
else r=mid;
}
ans[i]=getsum(l,i);
}
int lst=0;
i64 qans=0;
for(int i=1;i<=cnt;i++){
qans=qans+(po[i]-lst)*ans[i];
lst=po[i];
}
qans+=(m-lst)*ans[1];
cout<<qans;
return 0;
}
ABC429E
真正的红如温。后 50min 全是这个题。
你说得对但是 5min 想懂了再 5min 就写完了。但是它就是不过啊。
普通 BFS 题,每个 S 算贡献即可。用 BFS 处理出每个点到一个 S 的最短路和不同 S 的非严格次短路。
但就是这种东西我也能写挂把。
赛后又写了一遍就过了,我咋看不出这两种写法的区别呢?
code
Show me the code
#define rd read()
#define mkp make_pair
#define ls p<<1
#define rs p<<1|1
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#include<bits/stdc++.h>
using namespace std;
typedef long long i64;
typedef unsigned long long u64;
typedef unsigned int u32;
typedef __int128 i128;
i64 read(){
i64 x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
const int N=3e5;
vector<int> e[N];
int d1[N],d2[N];
int f1[N],f2[N];
struct work{
int v;
int w;
int id;
};
queue<work> q;
int main(){
int n;cin>>n;
int m;cin>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
string s;cin>>s;
s=' '+s;
memset(d1,0x3f,sizeof d1);
memset(d2,0x3f,sizeof d2);
for(int i=1;i<=n;i++){
if(s[i]=='S'){
q.push(work{i,0,i});
f1[i]=i;d1[i]=0;
}
}
while(q.size()){
int u=q.front().v;
int w=q.front().w;
int id=q.front().id;
q.pop();
w++;
for(int v:e[u]){
if(d1[v]==0x3f3f3f3f){
d2[v]=d1[v];d1[v]=w;
f1[v]=f2[v];f1[v]=id;
}
else if(d2[v]==0x3f3f3f3f&&f1[v]!=id){
d2[v]=w;f2[v]=id;
}
else continue;
q.push(work{v,w,id});
}
}
for(int i=1;i<=n;i++){
if(s[i]=='D'){cout<<d1[i]+d2[i]<<'\n';}
}
return 0;
}
ABC429F
线段树题。
首先观察性质,由于行只有三行,我们一定不会去往左移动的。
于是我们从左上到右下的路径一定可以被拆成许多段横着的线段,任意两个线段的交最多是 \(1\),也就是在同一列中换行的情况。
不只是左上到右下这两点,对于里面的每一个点对我们都能这么拆。
于是我们考虑把它设成状态,设 \(c_{i,j}\) 表示在某一个列区间里面,从最左边一列 \(i\) 行的点到最右边一列 \(j\) 行的点的最小步数。
然后你会发现两个相邻区间是可以合并的,就是类似 Floyd 一样,我们枚举这两个区间从那一行并起来的路径就行了。
于是这东西放到线段树上就对了。仅需单点修改和全局查询。
有意思的小技巧。
code
Show me the code
#define rd read()
#define mkp make_pair
#define ls p<<1
#define rs p<<1|1
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long i64;
typedef unsigned long long u64;
typedef unsigned int u32;
typedef __int128 i128;
i64 read(){
i64 x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
const int N=2e5+5;
string s[5];
struct seg{
int l;int r;
int c[4][4];
}t[N<<2];
void b(int p,int l,int r){
t[p].l=l;t[p].r=r;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
t[p].c[i][j]=INF;
}
if(l==r){
for(int i=1;i<=3;i++)for(int j=1;j<=3;j++){
bool ok=1;
for(int k=min(i,j);k<=max(i,j);k++){
if(s[k][l]=='#')ok=0;
}
if(ok)t[p].c[i][j]=abs(i-j);
else t[p].c[i][j]=INF;
}
return ;
}
int mid=l+r>>1;
b(ls,l,mid);b(rs,mid+1,r);
for(int k=1;k<=3;k++)
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
t[p].c[i][j]=min({0ll+t[ls].c[i][k]+t[rs].c[k][j]+1,0ll+t[p].c[i][j],0ll+INF});
}
return ;
}
void upd(int p,int pos,int x){
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
t[p].c[i][j]=INF;
}
if(t[p].l==t[p].r&&t[p].l==pos){
if(s[x][pos]=='.'){
s[x][pos]='#';
for(int i=1;i<=3;i++)for(int j=1;j<=3;j++){
bool ok=1;
for(int k=min(i,j);k<=max(i,j);k++){
if(s[k][pos]=='#')ok=0;
}
if(ok)t[p].c[i][j]=abs(i-j);
else t[p].c[i][j]=INF;
}
}
else{
s[x][pos]='.';
for(int i=1;i<=3;i++)for(int j=1;j<=3;j++){
bool ok=1;
for(int k=min(i,j);k<=max(i,j);k++){
if(s[k][pos]=='#')ok=0;
}
if(ok)t[p].c[i][j]=abs(i-j);
else t[p].c[i][j]=INF;
}
}
return ;
}
int mid=t[p].l+t[p].r>>1;
if(pos<=mid)upd(ls,pos,x);
if(mid<pos) upd(rs,pos,x);
for(int k=1;k<=3;k++)
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
t[p].c[i][j]=min({0ll+t[ls].c[i][k]+t[rs].c[k][j]+1,0ll+t[p].c[i][j],0ll+INF});
}
return ;
}
seg qry(){return t[1];}
int main(){
int n;cin>>n;
cin>>s[1]>>s[2]>>s[3];
s[1]=' '+s[1];s[2]=' '+s[2];s[3]=' '+s[3];
int q;cin>>q;
b(1,1,n);
for(int i=1;i<=q;i++){
int x,y;cin>>x>>y;
upd(1,y,x);
if(qry().c[1][3]>=INF)cout<< -1<<'\n';
else cout<<qry().c[1][3]<<'\n';
}
return 0;
}

浙公网安备 33010602011771号