DTOJ 2022.11.07 测试 题解
A
题目大意
长度为 \(n\) 的数列,给 \(m\) 条信息:从 \(x,y\) 开始的最长公共前缀的长度是 \(z\),要求字典序最小.
(\(1 \le m \le 1000\),\(1 \le n \le 1000\))
题解
每个信息可以被拆成最多 \(n\) 条信息,\(a_x=a_y,\ a_{x+1}=a_{y+1},\cdots,a_{x+z-1}=a_{y+z-1},a_{x+z}\ne a_{y+z}\)
这些是很好用并查集维护的.
首先把相等的下标合并起来,再判一下有没有不等关系在相同集合里,就可以判断可行性.
现在考虑字典序怎么最小.
直接把所有不等关系存起来,从前往后贪心填,每一个位置直接填最小合法的数就好了.
因为字典序本质就是贪心的所以这样一定就是对的.
测试的时候写挂了,但是后面经提醒改了((
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1005;
int n,m;
int fa[N],x[N],y[N],z[N],dat[N],v[N],cd;
int get(int x) { return x==fa[x]?x:(fa[x]=get(fa[x])); }
void merge(int x, int y) { x=get(x),y=get(y); if(x<y) swap(x,y); if(x!=y) fa[x]=y; }
vector<int> g[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) fa[i]=i;
for(int i=1; i<=m; i++) scanf("%d%d%d",&x[i],&y[i],&z[i]);
for(int i=1; i<=m; i++) for(int j=1; j<=z[i]; j++) merge(x[i]+j-1,y[i]+j-1);
for(int i=1; i<=m; i++)
if(x[i]+z[i]<=n and y[i]+z[i]<=n)
{
int tx=get(x[i]+z[i]),ty=get(y[i]+z[i]);
if(tx==ty) { puts("-1"); return 0; }
g[tx].push_back(ty),g[ty].push_back(tx);
}
//for(int i=1; i<=n; i++) printf("%d ",get(i)); puts("");
//for(int i=1; i<=2; i++) for(int v:g[i]) printf("%d -> %d\n",i,v);
for(int i=1; i<=n; i++) dat[i]=-1;
for(int i=1; i<=n; i++)
{
int x=get(i);
for(int j=0; j<N; j++) v[j]=0;
if(dat[x]==-1)
{
for(int y:g[x]) if(y<x) v[dat[y]]=1;
for(int j=0; j<N; j++) if(v[j]==0) { dat[x]=j; break; }
}
dat[i]=dat[x];
}
for(int i=1; i<=n; i++) printf("%d ",dat[i]); puts("");
return 0;
}
B
题解
bfs 求连通块,建边,每次询问暴力跑 dfs/bfs,看可达与否就好了
不知道为什么放在 B。。
二维压一维越来越熟练了hhh(写了好多这样的
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e4+5;
int n,m,k,q;
int a[N],col[N],nc;
vector<int> g[N];
int vis[N];
struct point
{
int x,y;
void rd() { scanf("%d%d",&x,&y); }
friend point operator+ (point A, point B) { return {A.x+B.x,A.y+B.y}; }
} dir[]={{1,0},{0,1},{-1,0},{0,-1}};
int F(point P) { return (P.x-1)*m+P.y; }
point G(int t) { return {(t-1)/m+1,(t-1)%m+1}; }
void bfs(point S, int c)
{
queue<point> q;
q.push(S); col[F(S)]=c;
auto check = [&] (const point &P) { return 1<=P.x and P.x<=n and 1<=P.y and P.y<=m and !col[F(P)] and a[F(P)]; };
while(!q.empty())
{
point x=q.front(); q.pop();
for(int i=0; i<4; i++)
{
point y=x+dir[i];
if(!check(y)) continue;
col[F(y)]=c; q.push(y);
}
}
}
void bfs2(int S, int c)
{
queue<int> q;
q.push(S);
while(!q.empty())
{
int u=q.front(); q.pop();
for(int v:g[u]) if(vis[v]!=c) vis[v]=c,q.push(v);
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&k,&q);
char ch=getchar();
for(int i=1; i<=n*m; )
{
while(ch!='.' and ch!='#') ch=getchar();
a[i++]=(ch=='.'); ch=getchar();
}
for(int i=1; i<=n; i++) for(int j=1; j<=m; j++)
if(a[F({i,j})] and !col[F({i,j})]) bfs({i,j},++nc);
nc--;
point u,v;
for(int i=1; i<=k; i++)
{
u.rd(),v.rd();
int tu=col[F(u)],tv=col[F(v)];
if(tu!=tv) g[tu].push_back(tv);
}
for(int i=1; i<=q; i++)
{
u.rd(),v.rd();
int tu=col[F(u)],tv=col[F(v)];
if(tu==tv) puts("1");
else bfs2(tu,i),printf("%d\n",(vis[tv]==i));
}
return 0;
}
C
第一眼维护凸壳,但是发现很难很难维护
这题应该用李超线段树或者cdq分治
但是 cdq 还没写过
应该会补一个李超线段树写法(
D
不可写
矩阵树定理
还没学