ABC 杂题
ABC186E Throne
有
很容易想到构造方程:
直接 exgcd
求逆元,算出在
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
void exgcd(int a,int b,int &inv,int &x,int &y){
if(!b) inv=a,x=1,y=0;
else exgcd(b,a%b,inv,y,x), y-=(a/b)*x;
}
int T;
signed main(){
cin>>T;
while(T--){
int n,s,k,inv,x,y;
cin>>n>>s>>k;
int gcd=__gcd(__gcd(n,s),k);
n/=gcd;s/=gcd;k/=gcd;
exgcd(k,n,inv,x,y);
if(inv!=1){
cout<<-1<<'\n';
}else{
inv=(x+n)%n;
cout<<(n-s)*inv%n<<'\n';
}
}
return 000;
}
ABC186F Rook on Grid
有一个
设
先处理出从左往右可以到达的格子数
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+34;
int n,m,k;
int x[maxn],y[maxn];
int ans=0;
int tree[maxn];
vector<int>query[maxn];
int lowbit(int x){return x&-x;}
void add(int pos,int c){pos++;for(;pos<=200030;pos+=lowbit(pos)) tree[pos]+=c;}
int q(int pos){pos++;int res=0;for(;pos;pos-=lowbit(pos)) res+=tree[pos];return res;}
signed main(){
cin>>n>>m>>k;
// assert(m<=n);
for(int i=1;i<=max(n,m);i++) x[i]=n+1;
for(int i=1;i<=max(n,m);i++) y[i]=m+1;
for(int i=1,X,Y;i<=k;i++){
cin>>X>>Y;
x[Y]=min(x[Y],X);
y[X]=min(y[X],Y);
}
for(int i=1;i<=x[1]-1;i++) ans+=y[i]-1, query[y[i]].emplace_back(i);
for(int i=x[1];i<=n;i++) query[1].emplace_back(i);
for(int i=1;i<y[1];i++){
for(int j:query[i]) add(j,1);
ans+=q(x[i]-1);
}
cout<<ans;
return 0;
}
ABC185E Sequence Matching
给一棵树,
- 对于编号为
的边,和其一个端点 ,将 不经过 就能到达的点的分数加上 。
求操作完后每个点的分数。
考虑进行标记(差分)。钦定 1 为根,处理出每个点的父亲,则对于每个询问,若
时间复杂度
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+3;
vector<int>e[maxn];
int tag[maxn],f[maxn],n,q;
struct{
int u,v;
}E[maxn];
void dfs(int u,int fa){
f[u]=fa;
for(int v:e[u]){
if(v!=fa) dfs(v,u);
}
}
void dfs1(int u,int fa){
for(int v:e[u]){
if(v!=fa){
tag[v]+=tag[u];
dfs1(v,u);
}
}
}
signed main(){
cin>>n;
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
E[i]={u,v};
e[u].emplace_back(v);
e[v].emplace_back(u);
}
dfs(1,0);
cin>>q;
while(q--){
int t,id,x,u,v;
cin>>t>>id>>x;
if(t==1){
u=E[id].u,v=E[id].v;
}else{
u=E[id].v,v=E[id].u;
}
if(u==f[v]){
tag[1]+=x;
tag[v]-=x;
}else{
tag[u]+=x;
}
}
dfs1(1,0);
for(int i=1;i<=n;i++){
cout<<tag[i]<<'\n';
}
return 0;
}
ABC367F Rearrange Query
给你两个序列
朴素的思路是
所以只能舍弃正确性,保证复杂度,将每一个数映射到一个随机数(这里将
时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+3;
int a1[maxn],a2[maxn],b1[maxn],b2[maxn];
int l,r,L,R,n,q;
const int mod1=1000000241;
const int mod2=1000000931;
int ha1(int x){
int ret=1,de=41;
for(;x;x>>=1,de=de*de%mod1) if(x&1) ret=ret*de%mod1;
return ret;
}
int ha2(int x){
int ret=1,de=37;
for(;x;x>>=1,de=de*de%mod2) if(x&1) ret=ret*de%mod2;
return ret;
}
signed main(){
cin>>n>>q;
for(int i=1,x;i<=n;i++){
cin>>x;
a1[i]=(a1[i-1]+ha1(x))%mod1;
// a2[i]=(a2[i-1]+ha2(x))%mod2;
}
for(int i=1,x;i<=n;i++){
cin>>x;
b1[i]=(b1[i-1]+ha1(x))%mod1;
// b2[i]=(b2[i-1]+ha2(x))%mod2;
}
while(q--){
cin>>l>>r>>L>>R;
if(R-L!=r-l){
cout<<"No\n";
continue;
}
int l1=(a1[r]-a1[l-1]+mod1)%mod1;
// int r1=(a2[r]-a2[l-1]+mod2)%mod2;
int l2=(b1[R]-b1[L-1]+mod1)%mod1;
// int r2=(b2[R]-b2[L-1]+mod2)%mod2;
if(l1==l2){
cout<<"Yes\n";
}else{
cout<<"No\n";
}
}
return 0;
}
// 单模数都能过
ABC365E Xor Sigma Problem
给你一个序列
由于异或的可加性,求异或和可以用前缀和处理掉,设前缀数组为
加法和异或并不具有交换结合律,但是可以发现每次的
最终的式子:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+3;
int a[maxn],b[maxn];
int n;
int ton[64];
bitset<32>bit[maxn];
signed main(){
cin>>n;
bit[0]=0;
for(int i=1,x;i<=n;i++){
cin>>x;
a[i]=x;
b[i]=b[i-1]^x;
bit[i]=b[i];
for(int j=0;j<31;j++){
ton[j]+=bit[i][j];
}
}
int ans=0;
for(int i=1;i<n;i++){
for(int j=0;j<31;j++){
ton[j]-=bit[i][j];
}
for(int j=0;j<31;j++){
int cnt=bit[i-1][j]?n-i-ton[j]:ton[j];
ans+=(1ll<<j)*cnt;
}
}
cout<<ans;
return 0;
}
三倍经验:
P9236 [蓝桥杯 2023 省 A] 异或和之和
ABC371E I Hate Sigma Problems
设
设
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+3;
int n,ans;
int a[maxn],f[maxn];
vector<int>v[maxn];
signed main(){
cin>>n;
for(int i=1;i<=n;i++) v[i].emplace_back(0);
for(int i=1;i<=n;i++){
cin>>a[i];
f[i]=v[a[i]].back();
v[a[i]].emplace_back(i);
}
for(int i=1;i<=n;i++){
ans=ans+(i-f[i])*(n-i+1);
}
cout<<ans;
return 0;
}
ABC268E Chinese Restaurant (Three Star Version)
给你一个排列
考虑到对于一个
由两段单调区间构成,分别单增和单减。我们可以统计初始态时上升与下降的个数之差
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+3;
int n,a[maxn],b[maxn],c[maxn],ad,de,sum,ans=0x3f3f3f3f3f3f3f3f;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
signed main(){
cin>>n;
if(n&1){
for(int i=0;i<n;i++){
cin>>a[i];
int dis=min(abs(a[i]-i),n-abs(a[i]-i));
int dit=min(abs(a[i]-(i+1)),n-abs(a[i]-(i+1)));
int pos1=a[i],pos2=(a[i]+n/2)%n,pos3=(a[i]+n/2+1)%n,dis1=pos1-i,dis2=pos2-i,dis3=pos3-i;
if(dis<dit) ad++;
else if(dis>dit) ad--;
if(i>pos1) dis1+=n;
if(i>pos2) dis2+=n;
if(i>pos3) dis3+=n;
q.push({dis1,2});
q.push({dis2,-1});
q.push({dis3,-1});
sum+=dis;
}
}else{
for(int i=0;i<n;i++){
cin>>a[i];
int dis=min(abs(a[i]-i),n-abs(a[i]-i));
int dit=min(abs(a[i]-(i+1)),n-abs(a[i]-(i+1)));
int pos1=a[i],pos2=(a[i]+n/2)%n,dis1=pos1-i,dis2=pos2-i;
if(dis<dit) ad++;
else ad--;
if(i>pos1) dis1+=n;
if(i>pos2) dis2+=n;
q.push({dis1,2});
q.push({dis2,-2});
sum+=dis;
}
}
while(!q.empty()&&!q.top().first) q.pop();
ans=sum;
for(int d=1;d<=n;d++){
sum+=ad;
while(!q.empty()&&q.top().first==d){
ad+=q.top().second;
q.pop();
}
ans=min(ans,sum);
}
cout<<ans;
return 0;
}
ABC374E Sensor Optimization Dilemma 2
唐氏 trick 题。
加工每个产品有
显然可以二分。二分一个
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=103;
int n,X;
int a[maxn],b[maxn],p[maxn],q[maxn];
bool check(int x){
int cost=0;
for(int i=1;i<=n;i++){
int nowcost=1e13;
int unit1=x/a[i]+1,unit2=x/b[i]+1;
for(int j=0;j<=min(unit1,min(unit2,100ll));j++){
int unitj1=(x-a[i]*(unit1-j)<=0?0:((x-a[i]*(unit1-j)-1)/b[i]+1));
int unitj2=(x-b[i]*(unit2-j)<=0?0:((x-b[i]*(unit2-j)-1)/a[i]+1));
int cost3=(unit1-j)*p[i]+q[i]*(unitj1),cost4=(unit2-j)*q[i]+p[i]*(unitj2);
nowcost=min(nowcost,min(cost3,cost4));
}
cost+=nowcost;
}
return cost<=X;
}
signed main(){
cin>>n>>X;
for(int i=1;i<=n;i++){
cin>>a[i]>>p[i]>>b[i]>>q[i];
}
int l=0,r=1e9,ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
l=mid+1;
ans=mid;
}else{
r=mid-1;
}
}
cout<<ans;
return 0;
}
ABC375F Road Blocked
给你一个无向图,
- 断掉编号为
的边; - 求
最短路,不存在输出 -1。
这个对操作 1 的限制不禁让人联想到 CSP 的插入排序那题。于是我们考虑只在操作 1 的时候用 Floyd 取一遍最短路,时间复杂度 (不是你 AT 神机怎么 8e9 都跑不过了),不可过。
考虑反过来操作,将断边变成加边。首先对删了所有操作 1 中的边的图上跑一遍全局 Floyd,然后考虑加边
之间的最短路;- 任意点到
的最短路; - 任意点到
的最短路; - 经过点
的最短路; - 经过点
的最短路。
第 1,4,5 三个影响直接更新即可,而对于 2,3 操作相当于 Floyd 枚举断点
点击查看代码
#include<bits/stdc++.h>
#define int long long
const int maxn=3000003;
const int inf=0x3f3f3f3f3f3f3f3f;
using namespace std;
int n,m,q;
struct edge{
int u,v,w;
}E[maxn],d[maxn];
int dis[303][303];
int op[maxn],x[maxn],y[maxn];
void floyd(){
memset(dis,0x3f,sizeof dis);
for(int i=1;i<=m;i++){
if(E[i].u) dis[E[i].u][E[i].v]=dis[E[i].v][E[i].u]=min(dis[E[i].u][E[i].v],E[i].w);
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
void update(int x){
dis[d[x].u][d[x].v]=dis[d[x].v][d[x].u]=min(dis[d[x].u][d[x].v],d[x].w);
for(int i=1;i<=n;i++) dis[d[x].u][i]=min(dis[d[x].u][i],d[x].w+dis[d[x].v][i]);
for(int i=1;i<=n;i++) dis[i][d[x].u]=min(dis[i][d[x].u],d[x].w+dis[i][d[x].v]);
for(int i=1;i<=n;i++) dis[d[x].v][i]=min(dis[d[x].v][i],d[x].w+dis[d[x].u][i]);
for(int i=1;i<=n;i++) dis[i][d[x].v]=min(dis[i][d[x].v],d[x].w+dis[i][d[x].u]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][d[x].u]+dis[d[x].u][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][d[x].v]+dis[d[x].v][j]);
}
int ans[maxn];
signed main(){
ios::sync_with_stdio(0);
cin>>n>>m>>q;
for(int i=1,u,v,w;i<=m;i++){
cin>>u>>v>>w;
E[i]={u,v,w};
}
for(int i=1;i<=q;i++){
cin>>op[i];
if(op[i]==1){
cin>>x[i];
d[i]=E[x[i]];
E[x[i]]={0,0,inf};
}else{
cin>>x[i]>>y[i];
}
}
floyd();
for(int i=q;i;i--){
if(op[i]==1){
update(i);
}else{
if(dis[x[i]][y[i]]==inf) ans[i]=-1;
else ans[i]=dis[x[i]][y[i]];
}
}
for(int i=1;i<=q;i++){
if(op[i]==2) cout<<ans[i]<<'\n';
}
return 0;
}
ABC375G Road Blocked 2
给你一个无向图,对于每一条边,判断删去该边后最短路长度是否会变化。
正解:先跑出
解:先跑出 tmd 正反边的树不同构调了几天终于调出来了啊。
点击查看代码
#include<bits/stdc++.h>
#define int long long
const int maxn=4e5+3;
const int inf=0x3f3f3f3f3f3f3f3f;
using namespace std;
int n,m;
struct Ed{
int u,v,w;
}E[maxn];
struct edge{
int v,w,id;
}up[maxn];
vector<edge>e[maxn],g[maxn];
int vis1[maxn],visn[maxn];
struct dst{
int id,dis;
bool operator<(const dst &o)const{return dis>o.dis;}
};
priority_queue<dst>q;
int dis1[maxn],disn[maxn];
void dij(){
memset(dis1,0x3f,sizeof dis1);
memset(disn,0x3f,sizeof disn);
q.push({1,dis1[1]=0});
while(!q.empty()){
dst u=q.top();
q.pop();
if(!vis1[u.id]){
vis1[u.id]=1;
for(edge v:e[u.id]){
if(dis1[v.v]>dis1[u.id]+v.w){
dis1[v.v]=dis1[u.id]+v.w;
up[v.v]={u.id,v.w,v.id};
q.push({v.v,dis1[v.v]});
}
}
}
}
for(int i=2;i<=n;i++){
edge to=up[i];
g[i].push_back(to);
g[to.v].push_back({i,to.w,to.id});
}
q.push({n,disn[n]=0});
while(!q.empty()){
dst u=q.top();
q.pop();
if(!visn[u.id]){
visn[u.id]=1;
for(edge v:e[u.id]){
if(disn[v.v]>disn[u.id]+v.w){
disn[v.v]=disn[u.id]+v.w;
q.push({v.v,disn[v.v]});
}
}
}
}
}
int stk[maxn],ttf[maxn],top,ot[maxn],ans[maxn],FA[2][19][maxn],len,dep[2][maxn],idx[maxn],gg,dfn[maxn];
void dfs1(int u,int fa,int id){
if(gg) return;
stk[++top]=id;
ttf[top]=u;
if(u==n){
len=top-1;
for(int i=2;i<=top;i++) ot[stk[i]]=1,idx[ttf[i]]=i-1,dfn[i-1]=stk[i];
gg=1;
return;
}
for(edge v:g[u]){
if(v.v!=fa)
dfs1(v.v,u,v.id);
}
top--;
}
void dfs2(int u,int fa,int mmp){
FA[mmp][0][u]=fa;
dep[mmp][u]=dep[mmp][fa]+1;
for(edge v:g[u]){
if(v.v!=fa){
dfs2(v.v,u,mmp);
}
}
}
void init(){
for(int o=0;o<=1;o++)
for(int j=1;j<=18;j++)
for(int i=1;i<=n;i++)
FA[o][j][i]=FA[o][j-1][FA[o][j-1][i]];
}
int lca(int u,int v,int mmp){
if(dep[mmp][u]<dep[mmp][v]) swap(u,v);
int step=dep[mmp][u]-dep[mmp][v];
for(int j=0;j<=18;j++)
if(step&(1<<j)) u=FA[mmp][j][u];
if(u==v) return u;
for(int j=18;~j;j--)
if(FA[mmp][j][u]!=FA[mmp][j][v])
u=FA[mmp][j][u],v=FA[mmp][j][v];
return FA[mmp][0][u];
}
#define ls (now<<1)
#define rs (now<<1|1)
int tr[maxn<<2],tag[maxn<<2];
void pushdown(int now){
if(tag[now]){
tr[ls]=tr[rs]=tag[ls]=tag[rs]=tag[now];
tag[now]=0;
}
}
void modify(int now,int l,int r,int L,int R){
if(L<=l&&r<=R){
tag[now]=tr[now]=1;
return;
}
int mid=(l+r)>>1;
pushdown(now);
if(L<=mid) modify(ls,l,mid,L,R);
if(mid+1<=R) modify(rs,mid+1,r,L,R);
}
void query(int now,int l,int r){
if(l==r){
ans[dfn[l]]=tr[now];
return;
}
int mid=(l+r)>>1;
pushdown(now);
query(ls,l,mid);
query(rs,mid+1,r);
}
signed main(){
cin>>n>>m;
for(int i=1,u,v,w;i<=m;i++){
cin>>u>>v>>w;
E[i]={u,v,w};
e[u].push_back({v,w,i});
e[v].push_back({u,w,i});
}
dij();
dfs1(1,0,0);
dfs2(1,0,0);
dfs2(n,0,1);
init();
for(int i=1;i<=m;i++){
if(!ot[i]){
ans[i]=1;
if(dis1[E[i].u]+E[i].w+disn[E[i].v]==dis1[n]){
int v1=lca(E[i].u,n,0),v2=lca(E[i].v,1,1);
if(idx[v1]+1<=idx[v2])
modify(1,1,len,idx[v1]+1,idx[v2]);
}
if(dis1[E[i].v]+E[i].w+disn[E[i].u]==dis1[n]){
int v1=lca(E[i].v,n,0),v2=lca(E[i].u,1,1);
if(idx[v1]+1<=idx[v2])
modify(1,1,len,idx[v1]+1,idx[v2]);
}
}
}
query(1,1,len);
for(int i=1;i<=m;i++){
if(!ans[i]) cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}
ABC203D Pond
给你一个
考虑到中位数的经典结论:有
于是二分中位数
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=803;
int a[maxn][maxn],b[maxn][maxn];
int n,k;
bool chk(int x){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
b[i][j]=b[i][j-1]+b[i-1][j]-b[i-1][j-1]+(a[i][j]>x);
for(int i=1;i+k-1<=n;i++)
for(int j=1;j+k-1<=n;j++)
if(b[i+k-1][j+k-1]-b[i+k-1][j-1]-b[i-1][j+k-1]+b[i-1][j-1]<=k*k/2)
return 1;
return 0;
}
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
int l=0,r=1e9,ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(chk(mid)){
r=mid-1;
ans=mid;
}else
l=mid+1;
}
cout<<ans;
return 0;
}
ABC232G Modulo Shortest Path
给你
前缀优化建图典题。
由于
:边权为 ; :边权为 。
对
可以建出以下图:(
可以发现图中存在负边权,而跑 SPFA 会被卡。
考虑把边权转正,将图中边
点击查看代码
#include<bits/stdc++.h>
#define int long long
const int maxn=5e5+3;
const int inf=0x3f3f3f3f3f3f3f3f;
using namespace std;
int n,m;
int b[maxn];
struct node{
int i,a,b;
bool operator<(const node &o)const{return b<o.b;}
}c[maxn];
struct edge{int v,w;};
vector<edge>e[maxn*3];
void add(int u,int v,int w){e[u].push_back({v,w});}
struct dist{
int id,dis;
bool operator<(const dist &o)const{return dis>o.dis;}
};
priority_queue<dist>q;
int dis[maxn*3],vis[maxn*3];
int pos1=0,posn=0;
void dij(){
for(int i=0;i<=3*n+10;i++) dis[i]=inf;
q.push({pos1,dis[pos1]=0});
while(!q.empty()){
dist u=q.top();
q.pop();
if(!vis[u.id]){
vis[u.id]=1;
for(edge v:e[u.id]){
if(dis[v.v]>dis[u.id]+v.w){
dis[v.v]=dis[u.id]+v.w;
q.push({v.v,dis[v.v]});
}
}
}
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>c[i].a, c[i].i=i;
for(int i=1;i<=n;i++) cin>>c[i].b;
sort(c+1,c+n+1);
for(int i=1;i<=n;i++){
if(c[i].i==1) pos1=i;
if(c[i].i==n) posn=i;
add(n+i,i,c[i].b);
add(2*n+i,i,0);
if(i<n) add(2*n+i,2*n+i+1,c[i+1].b-c[i].b);
if(i>1) add(n+i,n+i-1,0);
int pos=upper_bound(c+1,c+n+1,(node){0,0,m-c[i].a-1})-c-1;
if(pos>=1)add(i,n+pos,c[i].a);
// cerr<<i<<' '<<pos<<' '<<c[pos+1].b+c[i].a-m<<'\n';
if(pos<n) add(i,2*n+pos+1,c[pos+1].b+c[i].a-m);
}
dij();
cout<<dis[posn];
return 0;
}
ABC373G No Cross Matching
给你
考虑一个结论,做二分图最小权匹配,一定合法。 四个点
直接跑费用流
代码使用小码量常数小的调整法。过不了
点击查看代码
#include<bits/stdc++.h>
#define int long long
const int maxn=5e3+3;
const int inf=0x3f3f3f3f3f3f3f;
using namespace std;
int n,x[maxn],y[maxn];
int p[maxn];
double getdis(int x1,int y1,int x2,int y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
signed main(){
cin>>n;
for(int i=1;i<=2*n;i++){
cin>>x[i]>>y[i];
p[i]=i+n;
}
while(1){
int gg=0;
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
if(getdis(x[i],y[i],x[p[i]],y[p[i]])+getdis(x[j],y[j],x[p[j]],y[p[j]])>getdis(x[i],y[i],x[p[j]],y[p[j]])+getdis(x[j],y[j],x[p[i]],y[p[i]])){
swap(p[i],p[j]);
gg=1;
}
}
}
if(!gg) break;
}
for(int i=1;i<=n;i++) cout<<p[i]-n<<' ';
return 0;
}
ABC377G Edit to Match
给你
考虑建 trie 树的过程,实质是每次新建一条链。而我们要求的就是当前字符串
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+7;
char s[maxn];
int n,trie[maxn][26],val[maxn],id[maxn],node,fa[maxn*26];
int ins(){
int lens=strlen(s);
int u=0,res=0;
for(int i=0;i<lens;i++){
if(trie[u][s[i]-'a']==-1)
trie[u][s[i]-'a']=++node;
fa[trie[u][s[i]-'a']]=u,u=trie[u][s[i]-'a'];
val[u]=min(val[u],val[fa[u]]+1);
}
res=val[u];
val[u]=0;
for(;fa[u];u=fa[u])
val[fa[u]]=min(val[fa[u]],val[u]+1);
return res;
}
signed main(){
cin>>n;
memset(val,0x3f,sizeof val);
memset(trie,-1,sizeof trie);
val[0]=0;
for(int i=1;i<=n;i++){
cin>>s;
cout<<ins()<<'\n';
}
return 0;
}
ABC377F Avoid Queen Attack
给你一个
懒得写了,贺了官方题解。
点击查看代码
#include <iostream>
#include <set>
int main(){
using namespace std;
unsigned N, Q;
cin >> N >> Q;
// horizontal, vertical, and (anti)diagonal effects
set<unsigned> horizontal, vertical, diagonal_slash, diagonal_backslash;
for (unsigned i{}; i < Q; ++i){
unsigned x, y;
cin >> x >> y;
horizontal.emplace(x);
vertical.emplace(y);
diagonal_slash.emplace(x + y);
diagonal_backslash.emplace(x - y);
}
// vertical and horizontal effects can be immediately counted
unsigned long ans{(N - size(horizontal)) * (N - size(vertical))};
// anti-diagonal(with constant x + y)
for (const auto d : diagonal_slash) {
// the x coordinates of already inspected squares
set<unsigned> cross_x;
for (const auto x : horizontal)
// insert if the intersection is inside
if (1 <= d - x && d - x <= N)
cross_x.emplace(x);
for (const auto y : vertical)
// insert if the intersection is inside
if (1 <= d - y && d - y <= N)
cross_x.emplace(d - y);
// # squres (2 min(d, N + 1) - d - 1) subtracted by # already inspected squares is # newly found squares (# means "the number of")
ans -= 2 * min(d, N + 1) - d - 1 - size(cross_x);
}
// diagonal(with constant x - y)
for (const auto d : diagonal_backslash) {
// the x coordinates of already inspected squares
set<unsigned> cross_x;
for (const auto x : horizontal)
// insert if the intersection is inside
if (1 <= x - d && x - d <= N)
cross_x.emplace(x);
for (const auto y : vertical)
// insert if the intersection is inside
if (1 <= d + y && d + y <= N)
cross_x.emplace(d + y);
for (const auto e : diagonal_slash)
// insert if the intersection is inside
if ((d + e) % 2 == 0 && 1 <= (d + e) / 2 && (d + e) / 2 <= N && 1 <= (e - d) / 2 && (e - d) / 2 <= N)
cross_x.emplace((d + e) / 2);
// # squares N - |d| subtracted by # already inspected squares is # newly found squares
ans -= N - min(d, -d) - size(cross_x);
}
cout << ans << endl;
return 0;
}
ABC371G Lexicographically Smallest Permutation
给你两个排列
- 将所有
变为 。
求可以得到的字典序最小的
建图,连边
开一个数组
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+7;
int n;
int p[maxn],a[maxn];
vector<int>v[maxn];
int ans[maxn],pos[maxn],siz[maxn],scccnt,mi[maxn],req[maxn];
vector<int>d[maxn];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>p[i];
req[i]=0-1;
if(i>1) if(d[i].empty())
for(int k=i;k<=n;k*=i) for(int j=k;j<=n;j+=k)
d[j].emplace_back(k);
}
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
if(!pos[i]){
scccnt++;
int mix=n+1;
for(int j=i;!pos[j];j=p[j]){
v[scccnt].emplace_back(j);
pos[j]=++siz[scccnt];
}
for(int j=0;j<siz[scccnt];j++){
int flag=1;
for(int k:d[siz[scccnt]]){
if(req[k]!=-1&&j%k!=req[k]){
flag=0;
break;
}
}
if(flag)
if(mix==n+1||a[v[scccnt][j]]<a[v[scccnt][mix]])
mix=j;
}
for(int j=0;j<siz[scccnt];j++)
ans[v[scccnt][j]]=a[v[scccnt][(j+mix)%siz[scccnt]]];
for(int k:d[siz[scccnt]]) req[k]=mix%k;
}
}
for(int i=1;i<=n;i++)
cout<<ans[i]<<' ';
return 0;
}
ABC369G As far as possible
给你一棵以 1 为根的树,对于每个
贪心地选最长的链,删去,再选最长的链,如此下去一定最优。实质上是长链剖分的过程。时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+3;
struct edge{
int v,w;
};
vector<edge>e[maxn];
int dis[maxn],siz[maxn],n,fa[maxn],dfncnt,son[maxn],w[maxn];
void dfs1(int u,int Fa){
for(edge v:e[u]){
if(v.v!=Fa){
w[v.v]=v.w;
dfs1(v.v,u);
if(siz[v.v]+v.w>siz[u]) siz[u]=siz[v.v]+v.w,son[u]=v.v;
}
}
}
void dfs2(int u,int t){
dis[t]+=w[u];
if(son[u]) dfs2(son[u],t);
for(edge v:e[u]){
if(v.v!=fa[u]&&v.v!=son[u]){
dfs2(v.v,v.v);
}
}
}
signed main(){
cin>>n;
for(int i=1,u,v,w;i<n;i++){
cin>>u>>v>>w;
e[u].push_back({v,w});
e[v].push_back({u,w});
}
dfs0(1,0);
dfs1(1,0);
dfs2(1,1);
sort(dis+1,dis+n+1,greater<int>());
int ans=0;
for(int i=1;i<=n;i++){
ans+=dis[i];
cout<<2*ans<<'\n';
}
return 0;
}
ABC380G Another Shuffle Window
给你一个长为
诈骗题 + 妙妙题。对于一个无重复元素的序列重排,期望逆序对数为
证明:考虑对于一个数对
是逆序对的概率为 ,数对数量为 ,证毕。
所以先求出全局的逆序对数,然后枚举每个窗口,则期望逆序对数为
区间逆序对数可以维护一个树状数组,删掉开头相当于减掉窗口中小于它的数的数量。加上结尾相当于加上区间中大于它的数的数量。总时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+7;
const int mod=998244353;
int n,k;
int a[maxn],sum;
int qpow(int a,int b){
int res=1;
for(;b;b>>=1,a=a*a%mod) if(b&1) res=res*a%mod;
return res;
}
int tr[maxn];
#define lowbit(x) (x&-x)
void add(int x,int c){for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;}
int query(int x){
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
int quer1(int x){
int res=query(n)-query(x-1);
return res;
}
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=n;i;i--){
sum+=query(a[i]);
add(a[i],1);
}
for(int i=n;i;i--) tr[i]=0;
for(int i=1;i<=n;i++){
sum+=quer1(a[i]);
add(a[i],1);
}
for(int i=n;i;i--) tr[i]=0;
int E=0,p2=qpow(2,mod-2),pn=qpow(n-k+1,mod-2),now=sum;
for(int i=1;i<=k;i++) now-=2*quer1(a[i]),add(a[i],1);
for(int i=1;i<=n-k+1;i++){
E=(E+pn*(now%mod+k*(k-1)%mod*p2%mod)%mod)%mod;
now+=2*query(a[i]-1); add(a[i],-1);
if(i+k<=n) now-=2*quer1(a[i+k]),add(a[i+k],1);
}
cout<<E*p2%mod;
return 0;
}
ABC383E Sum of Max Matching
又是一年退役季。一边上课一边口胡。
定义路径的权值为路径上的最大边权。定义
给你一个带权无向图,给出两个序列
考虑向图上按边权从小到大加边,设当前边权为 vector二元权值
hanhoudedidue 怒斥此为 KK 重构树板,让我们膜拜他!
bonus:如果固定
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+7;
int n,m,k,A[maxn],B[maxn];
struct eE{
int u,v,w;
bool operator<(const eE &o)const{
return w<o.w;
}
}E[maxn];
struct mst{
int fa[maxn],a[maxn],b[maxn];
void init(int n){for(int i=0;i<=n;i++) fa[i]=i,a[i]=A[i],b[i]=B[i];}
int find(int x){while(fa[x]!=x) x=fa[x]=fa[fa[x]]; return x;}
int merge(int x,int y,int w){ // fa[y]=x;
int fx=find(x),fy=find(y);
a[fx]+=a[fy]; a[fy]=0;
b[fx]+=b[fy]; b[fy]=0;
fa[fy]=fx; int res=min(a[fx],b[fx]);
a[fx]-=res; b[fx]-=res;
return res*w;
}
bool q(int x,int y){ return find(x)==find(y); }
}s;
signed main(){
cin>>n>>m>>k;
for(int i=1;i<=m;i++)
cin>>E[i].u>>E[i].v>>E[i].w;
for(int i=1,x;i<=k;i++)
cin>>x, A[x]++;
for(int i=1,x;i<=k;i++)
cin>>x, B[x]++;
s.init(max(n,k));
sort(E+1,E+m+1);
int ans=0;
for(int i=1;i<=m;i++)
if(!s.q(E[i].u,E[i].v))
ans+=s.merge(E[i].u,E[i].v,E[i].w);
cout<<ans;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!