20231027 比赛总结

比赛链接

反思

A

感觉不难,出了点小问题也及时解决了,感觉不错

B

对标去年 N O I P    T 2 NOIP\;T2 NOIPT2 是吧,卡了我 2 h 2h 2h 不会,一直在想如何构造,甚至开始搜哈密顿路的定理( d i r a c dirac dirac 定理),感觉正式比赛中应该先跳,失策了

C

果然对标 N O I P 2022 NOIP2022 NOIP2022,比 T 2 T2 T2 简单很多,有一个比较显然的贪心写法,但数据很水,随便怎么都能过(指时间),但 c a i = c b i c_{a_i}=c_{b_i} cai=cbi 的部分没有处理好,导致 − 50 p t s -50pts 50pts

D

没看

题解

A

直接跑最短路

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
const int N=600100;
typedef pair<int,int> pii;
int a,b,dis[N];
bool vis[N];
priority_queue<pii,vector<pii>,greater<pii> > pq;
inline int read(){
    int FF=0,RR=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    return FF*RR;
}
void upd(int x,int Dist){
    if(dis[x]>Dist) dis[x]=Dist,pq.push({Dist,x});
}
int main(){
    freopen("perfect.in","r",stdin);
    freopen("perfect.out","w",stdout);
    a=read(),b=read();
    memset(dis,0x3f,sizeof(dis));
    dis[a]=0,pq.push({0,a});
    if(a) dis[0]=3,pq.push({3,0});
    while(!pq.empty()){
        int u=pq.top().second;pq.pop();
        if(vis[u]) continue;
        vis[u]=1;
        if(u) for(int d=2;u*d<N;d++) upd(u*d,dis[u]+4+(d-1)*2);
        if(u+1<N) upd(u+1,dis[u]+1);
        if(u) upd(u-1,dis[u]+1);
    }
    printf("%d\n",dis[b]);
    fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    return 0;
}

B

很妙的一道题,但是是
我们需要维护处一条 R R R 链和一条 B B B 链,链上颜色都为 R e d Red Red B l u e Blue Blue
考虑增量法
如果我们现在拥有 R R R u → x u\to x ux B B B v → y v\to y vy,需要新增一个点 z z z
即下图中的情况:
在这里插入图片描述如果 x → z x\to z xz R R R y → z y\to z yz B B B 直接接上即可
现在需要考虑的是 x → z x\to z xz B B B y → z y\to z yz B B B x → y x\to y xy 的颜色是没有本质区别的,可以只考虑 R R R 的情况
不难想到直接 R R R 链为 u → x → y → z u\to x\to y\to z uxyz,然后把 B B B 链中删除 y y y 即可

现在我们的问题是如何保证当前枚举的起点 S t St St 为一条链的链头,显然只有当这条链删空时 S t St St 会改变位置,不难发现, S t St St 会变成另一条链的链尾,直接 r e v e r s e reverse reverse 即可
时间复杂度 O ( n 2 ) O(n^2) O(n2)

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define pb emplace_back
using namespace std;
const int N=2100;
int n;
bool col[N][N];
vector<int> R,B;
char str[N];
inline int read(){
    int FF=0,RR=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    return FF*RR;
}
void solve(int St){
    R.clear(),B.clear();
    R.pb(St);
    bool cur=0;
    for(int i=1;i<=n;i++){
        if(i==St) continue;
        if(B.empty()){ B.pb(i);continue;}
        if(R.empty()){ R.pb(i);continue;}
        int x=R.back(),y=B.back(),z=i;
        if(!col[x][z]){ R.pb(z);continue;}
        if(col[y][z]){ B.pb(z);continue;};
        if(!col[x][y]){
            if(cur&&B.size()==1){
                R.pb(y),reverse(R.begin(),R.end());B.pop_back(),B.pb(z);
                cur^=1;
            }
            else B.pop_back(),R.pb(y),R.pb(z);
        }
        else{
            if(!cur&&R.size()==1){
                B.pb(x),reverse(B.begin(),B.end());R.pop_back(),R.pb(z);
                cur^=1;
            }
            else R.pop_back(),B.pb(x),B.pb(z);
        }
    }
    if(!cur){
        for(int ans:R) printf("%d ",ans);
        for(int ans:B) printf("%d ",ans);
    }
    else{
        for(int ans:B) printf("%d ",ans);
        for(int ans:R) printf("%d ",ans);
    }
}
int main(){
    freopen("hamil.in","r",stdin);
    freopen("hamil.out","w",stdout);
    n=read();
    for(int i=2;i<=n;i++){
        scanf("%s",str+1);
        for(int j=1;j<i;j++) col[i][j]=col[j][i]=str[j]=='R';
    }
    for(int i=1;i<=n;i++) printf("%d\n",n),solve(i),puts("");
    fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    return 0;
}

C

因为加了 n i n^i ni 的系数,且每个数都 ≥ n \ge n n,所以我们考虑贪心
第一反应应该是建图(虽然我不是),考虑 b i → a i b_i\to a_i biai 连边,那么这就是基环树和树森林,考虑一个基环树是无法交换的,所以只可能是树上交换
不难发现,树上交换的一定是一条从根开始的链
根据这个性质,如果 c a i = c b i c_{a_i}=c_{b_i} cai=cbi,直接跳过
c a i > c b i c_{a_i}>c_{b_i} cai>cbi,打上标记, a a a 不能被交换
c a i < c b i c_{a_i}<c_{b_i} cai<cbi,我们每次暴力一点每次直接往上跳,现在的问题是如何判断是否可以跳,用数据结构不难维护
时间复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
#define pb push_back
#define lowbit(x) x&-x
using namespace std;
const int N=100100;
int n,m,k,a[N],b[N],c[N];
bool tag[N],good[N],vis[N],isrev[N];
int V,E,rt[N],ee[N];
int dfn[N],dfs_clock,siz[N],depth[N],up[N][20];
vector<int> G[N],ans;
inline int read(){
    int FF=0,RR=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    return FF*RR;
}
struct BIT{
	int tr[N];
	void add(int x,int v){ for(;x<=n;x+=lowbit(x)) tr[x]+=v;}
	int ask(int x){
		int res=0;
		for(;x;x-=lowbit(x)) res+=tr[x];
		return res;
	}
}BIT;
void dfs(int u,int Rt){
	V++,vis[u]=1,rt[u]=Rt;
	for(int v:G[u]) E+=!vis[v];
	for(int v:G[u]) if(!vis[v]) dfs(v,Rt);
}
void dfs_prev(int u){
	depth[u]=depth[up[u][0]]+1,dfn[u]=++dfs_clock,siz[u]=1;
	for(int v:G[u]) up[v][0]=u,dfs_prev(v),siz[u]+=siz[v];
}
int get_lca(int x,int y){
    if(depth[x]>depth[y]) swap(x,y);
    for(int i=18;i>=0;i--) if(depth[up[y][i]]>=depth[x]) y=up[y][i];
    if(x==y) return x;
    for(int i=18;i>=0;i--) if(up[x][i]!=up[y][i]) x=up[x][i],y=up[y][i];
    return up[x][0];
}
int get_root(int x){ return x==rt[x]?x:rt[x]=get_root(rt[x]);}
void make_tag(int x){ BIT.add(dfn[x],1),BIT.add(dfn[x]+siz[x],-1);}
int get_dist(int x,int y){
	int lca=get_lca(x,y);
	if(BIT.ask(dfn[x])+BIT.ask(dfn[y])-2*BIT.ask(dfn[lca])) return 1e9;
	return depth[x]+depth[y]-2*depth[lca];
}
int main(){
	freopen("equipment.in","r",stdin);
	freopen("equipment.out","w",stdout);
	n=read(),m=read(),k=read();
	for(int i=1;i<=n;i++) c[i]=read();
	for(int i=1;i<=n;i++) tag[i]=1;
	for(int i=1;i<=m;i++) a[i]=read(),b[i]=read(),G[b[i]].pb(a[i]),ee[a[i]]=i,tag[a[i]]=0;
	for(int i=1;i<=n;i++)
		if(tag[i]){
			V=E=0,dfs(i,i);
			if(V==E) continue;
			good[i]=1;
			dfs_prev(i);
		}
	for(int j=1;j<=18;j++) for(int i=1;i<=n;i++) up[i][j]=up[up[i][j-1]][j-1];
	for(int i=m;i;i--){
		if(isrev[i]||!good[rt[a[i]]]||c[a[i]]==c[b[i]]) continue;
		if(c[a[i]]>c[b[i]]) make_tag(a[i]);
		else if(get_dist(a[i],get_root(rt[a[i]]))<=k){
			int root=get_root(rt[a[i]]);
			vector<int> tmp;
			for(int x=a[i];x!=root;x=up[x][0]) tmp.pb(ee[x]),isrev[ee[x]]=1;
			reverse(tmp.begin(),tmp.end());
			for(int x:tmp) ans.pb(x),k--;
			make_tag(a[i]);
			rt[root]=rt[a[i]]=a[i],good[a[i]]=1;
		}
	}
	printf("%d\n",ans.size());
	for(int x:ans) printf("%d\n",x);
    fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值