比赛链接
反思
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
u→x,
B
B
B 链
v
→
y
v\to y
v→y,需要新增一个点
z
z
z
即下图中的情况:
如果
x
→
z
x\to z
x→z 为
R
R
R 或
y
→
z
y\to z
y→z 为
B
B
B 直接接上即可
现在需要考虑的是
x
→
z
x\to z
x→z 为
B
B
B 且
y
→
z
y\to z
y→z 为
B
B
B ,
x
→
y
x\to y
x→y 的颜色是没有本质区别的,可以只考虑
R
R
R 的情况
不难想到直接
R
R
R 链为
u
→
x
→
y
→
z
u\to x\to y\to z
u→x→y→z,然后把
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
bi→ai 连边,那么这就是基环树和树森林,考虑一个基环树是无法交换的,所以只可能是树上交换
不难发现,树上交换的一定是一条从根开始的链
根据这个性质,如果
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;
}