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
*/
posted on 2022-10-10 18:56  HZOI-曹蓉  阅读(70)  评论(0编辑  收藏  举报