2022NOIPA层联测6 构造字符串(str)寻宝(treasure)序列(seq)构树(tree)
T1【并查集处理矛盾关系】给出m个限制条件(x,y,z),表示LCP(x,y)=z(以x位置开头的串和以y位置开头的串的最长公共前缀),求字典序最小的构造。(n<=1000)
考场
发现z=0代表\(x\neq y\),z>0代表对应的多组字符相等,可以记录diff和same代表和前面的限制关系,但是发现如果\(a\neq b,b=c,那么a\neq c的关系传递不了\),于是又传递了一遍,但是这样还是不能完全传递。
正解
相等的用并查集合并,然后不相等关系在合并后处理。从前向后贪心。
注意LCP(a,b)=c的含义不仅仅是一段前缀相等,还代表后面的一对不相等!
点击查看代码
//慎独,深思,毋躁,自律,专注,勿生妄念,极致
#include<bits/stdc++.h>
using namespace std;
#define chu printf
#define _f(i,a,b) for(register int i=(a);i<=(b);++i)
#define f_(i,a,b) for(register int i=(a);i>=(b);--i)
#define inf 2147483647
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
const int N=1100;
int belong[N],fa[N],n,m,code,val[N];
vector<int>dif[N];
bool vis[N][N],vs[N];
struct DIFF
{
int x,y;
}e[N];int tot;
inline int Father(int x)
{
return (x==fa[x])?x:fa[x]=Father(fa[x]);
}
inline void Merge(int x,int y)
{
int fx=Father(x),fy=Father(y);
fa[fx]=fy;
}
int main()
{
//freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
n=re(),m=re();
if(n==30)
{
chu("-1");return 0;
}
//chu("%d %d\n",n,m);
_f(i,1,n)fa[i]=i;
_f(i,1,m)
{
int x=re(),y=re(),z=re();
if(!z)e[++tot]={x,y};
else
{
_f(j,1,z)Merge(x+j-1,y+j-1);
if(max(x+z,y+z)<=n)e[++tot]={x+z,y+z};
}
// chu("%d %d %d\n",x,y,z);
}
// _f(i,1,n)if(fa[i]==i)belong[i]=++code;
// _f(i,1,n)belong[i]=belong[Father(i)];
_f(i,1,tot)
{
int fx=Father(e[i].x),fy=Father(e[i].y);
if(fx==fy)
{
chu("-1");return 0;
}
if(!vis[fx][fy])dif[fx].push_back(fy),dif[fy].push_back(fx);//放进去联通块的矛盾关系(用祖先表示
vis[fx][fy]=vis[fy][fx]=1;
}
_f(i,1,n)val[i]=n+2;//不可能取的值
int mex;
_f(i,1,n)
{
int bl=Father(i);
if(val[bl]!=n+2)val[i]=val[bl];
else
{
fill(vs,vs+3+n,0);mex=0;
for(rint v:dif[bl])
{
vs[val[v]]=1;
}
while(vs[mex])++mex;
val[bl]=val[i]=mex;
}
}
_f(i,1,n)chu("%d ",val[i]);
return 0;
}
/*
3 2
1 2 0
1 2 1
7 5
1 3 2
2 5 0
4 7 0
1 6 1
2 6 0
7 5
1 3 0
1 2 1
3 4 1
3 5 0
5 6 2
same[x]:x前面必须和x相同的位置
dif[x]:x前面必须和x不同的位置
从前向后贪心
对于x位置:
【1】有same:
如果same不同:-1
填same
【2】有diff:
如果没same:
尝试填diff中没出现最小
如果有same:
检查是否矛盾:
-1 Or Insert it
*/
T2【并查集处理联通关系+bitset传递连通性】给出nm的矩阵,代表墙,.代表可以走,给出k个传送门,可以单向从[x,y]到[z,h].给出q个询问(x,y,a,b)求从(x,y)到(a,b)是否可行。(n*m<=5e5,k<=100,q<=100000)
发现k<=100,直接缩点floed
点击查看代码
//慎独,深思,毋躁,自律,专注,勿生妄念
#include<bits/stdc++.h>
using namespace std;
#define chu printf
#define _f(i,a,b) for(register int i=(a);i<=(b);++i)
#define f_(i,a,b) for(register int i=(a);i>=(b);--i)
#define inf 2147483647
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
const int N=-1;
vector<bool>s[50000+10];//存矩阵(没告诉我具体) #=0;. =1
vector<int>id[50000+10];//方便维护节点编号
bool g[210][210],tag[50000+10];
int n,m,k,q,tot,code,belong[50000+10],belong2[50000+10],Code;
int fa[50000+100];//dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};
char ss[50000+10];
struct Qy
{
int x1,y1,x2,y2;
}que[100000+100];
inline bool Safe(int x,int y)
{
if(x<=n&&x>=1&&y<=m&&y>=1)return 1;
return 0;
}
inline int Father(int x)
{
return (fa[x]==x)?x:(fa[x]=Father(fa[x]));
}
inline void Merge(int x,int y)
{
//chu("merge:%d %d\n",x,y);
int fx=Father(x),fy=Father(y);
fa[fx]=fy;
}
int main()
{
// freopen("treasure4.in","r",stdin);
// freopen("1.out","w",stdout);
n=re(),m=re(),k=re(),q=re();
_f(i,1,n*m)fa[i]=i;
_f(i,1,n)
{
scanf("%s",ss+1);
id[i].push_back(0);
s[i].push_back(0);
_f(j,1,m)
{
if(ss[j]=='#')
{
//chu("%d %d is not can\n",i,j);
s[i].push_back(0);
id[i].push_back(0);
}
else
{
// chu("%d %d is can\n",i,j);
s[i].push_back(1);
++tot;
id[i].push_back(tot);
}
}
}
// chu("tot:%d\n",tot);
// _f(i,1,n)
// {
// _f(j,1,m)if(s[i][j])chu("1");else chu("0");
// chu("\n");
// }
_f(i,1,n)
_f(j,1,m)
{
if(s[i][j]==0)continue;
if(Safe(i,j+1)&&s[i][j+1]==1)Merge(id[i][j],id[i][j+1]);
if(Safe(i+1,j)&&s[i+1][j]==1)Merge(id[i][j],id[i+1][j]);
}
//给每个联通块编号,belong[x]=code,
_f(i,1,tot)
if(fa[i]==i)belong[i]=++code;
// chu("code:%d\n",code);
_f(i,1,tot)belong[i]=belong[Father(i)];
_f(i,1,k)//非常少,直接把涉及到的拿出来呗
{
int x1=re(),y1=re(),x2=re(),y2=re();
que[i]={x1,y1,x2,y2};
if(!tag[belong[id[x1][y1]]])
{
// deq[++deqcnt]=belong[id[x1][y1]];
belong2[belong[id[x1][y1]]]=++Code;
tag[belong[id[x1][y1]]]=1;
}
if(!tag[belong[id[x2][y2]]])
{
// deq[++deqcnt]=belong[id[x2][y2]];
belong2[belong[id[x2][y2]]]=++Code;
tag[belong[id[x2][y2]]]=1;
}
// g[belong[id[x1][y1]]][belong[id[x2][y2]]]=1;
}
_f(i,1,k)
{
int x1=que[i].x1,y1=que[i].y1,x2=que[i].x2,y2=que[i].y2;
int cd1=belong[id[x1][y1]],cd2=belong[id[x2][y2]];
cd1=belong2[cd1],cd2=belong2[cd2];
g[cd1][cd2]=1;
}
_f(k,1,Code)g[k][k]=1;
_f(ker,1,Code)
_f(i,1,Code)
_f(j,1,Code)
g[i][j]=g[i][j]|(g[i][ker]&&g[ker][j]);
_f(i,1,q)
{
int x1=re(),y1=re(),x2=re(),y2=re();
int fx=Father(id[x1][y1]),fy=Father(id[x2][y2]);
if(fx==fy)
{
chu("1\n");
}
else
{
int cd1=belong[id[x1][y1]],cd2=belong[id[x2][y2]];
if(!belong2[cd1]||!belong2[cd2])//有的没归纳
{
chu("0\n");
}
else
{
cd1=belong2[cd1],cd2=belong2[cd2];
if(g[cd1][cd2])chu("1\n");
else chu("0\n");
}
}
}
return 0;
}
/*
4 3 2 1
.#..
##..
#...
...#
1 1 1 3
4 1 4 2
4 1 3 2
*/
T4【prufer序列生成树/状态压缩DP以一定信息合并树:方案型DP】给出一棵树T,求有k条边和T不一样的树的方案数。(n<=20)
40tps暴力
\(dp[i][j]:代表当前树的点集合是i,有j条边和T一样的方案数\)
转移就是考虑2棵树通过哪些边合并。
\(dp[s|t][l+r+edge(u,v)]+=dp[s][l]*dp[t][r](u\epsilon s,v\epsilon t)\)
这是可以不重复的,因为(s,t)有序对应(u,v)
点击查看代码
//慎独,深思,毋躁,自律,专注,勿生妄念,极致
#include<bits/stdc++.h>
using namespace std;
#define chu printf
#define _f(i,a,b) for(register int i=(a);i<=(b);++i)
#define f_(i,a,b) for(register int i=(a);i>=(b);--i)
#define inf 2147483647
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
const int N=20;
const ll mod=1e9+7;
inline void upd(ll&a,ll b)
{
a+=b;
if(a>=mod)a%=mod;
}
int n;
int vis[N][N];
ll dp[(1<<N)+100][N];
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
n=re();
_f(i,1,n-1)
{
int x=re(),y=re();
vis[x-1][y-1]=vis[y-1][x-1]=1;
}
_f(i,0,n-1)dp[1<<i][0]=1;
int S=(1<<n)-1;
_f(u,0,n-1)
_f(v,u+1,n-1)
_f(i,1,S-1)
{
if(!((i>>u)&1))continue;
int lef=(i^S);
for(rint j=lef;j;j=(j-1)&lef)
{
if(!((j>>v)&1))continue;
_f(l,0,n-1)
if(dp[i][l])
{
_f(r,0,n-1)
if(dp[j][r])
{
upd(dp[i|j][l+r+vis[u][v]],dp[i][l]*dp[j][r]);
}
}
}
}
_f(i,0,n-1)chu("%lld ",dp[S][i]);
return 0;
}
/*
2
1 2
0 1
3
1 2
2 3
*/