返回上一页

8.16暑假集训4

题面

下发文件和题解

A.打地鼠

很水,二维前缀和累加即可.时间复杂度

B.竞赛图

的暴力应该都会,枚举每一个子图,每次跑一遍

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 51
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|48);
}
ll t,n,ans;
ll dfn[maxn],low[maxn],tot;
ll mp[maxn][maxn],mp1[maxn][maxn];
bool vis[maxn];
bool fl[maxn];
bool flag[maxn];
ll num[maxn];
stack<ll> s;
ll cnt;
ll h;
static inline void clear()
{
	cnt=tot=0;while(!s.empty()) s.pop();
	memset(fl,0,sizeof(fl));
	memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));
}
static inline void tarjan(rll x)
{
	dfn[x]=low[x]=++tot;s.push(x);fl[x]=1;
	for(rll i=1;i<=n;i++)
	{
		if(!mp1[x][i]) continue;
		if(!dfn[i]) tarjan(i),low[x]=min(low[x],low[i]);
		else if(fl[i]) low[x]=min(low[x],dfn[i]);
	}
	if(dfn[x]==low[x])
	{
		rll t;cnt++;
		do
		{
			t=s.top();s.pop();fl[t]=0;
		} while(t!=x);
	}
}
static inline void dfs(rll x,rll d,rll sz)
{
	flag[x]=1;
	if(d==sz)
	{
		clear();
		for(rll i=1;i<=n;i++)
			for(rll j=1;j<=n;j++)
				if(flag[i]&&flag[j]) mp1[i][j]=mp[i][j];
				else mp1[i][j]=0;
		for(rll i=1;i<=n;i++) if((flag[i])&&!dfn[i]) tarjan(i);
		if(cnt==1) ans++;
		flag[x]=0;
		return;
	}
	for(rll i=x+1;i<=n;i++) if(!flag[i]) dfs(i,d+1,sz);
	flag[x]=0;
}
int main()
{
	t=read();
	while(t--)
	{
		ans=0;n=read();
		for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) mp[i][j]=read();
		for(rll i=1;i<=n;i++)
			for(rll j=1;j<=n;j++)
				dfs(j,1,i);
		write(ans+1);puts("");
	}
	return 0;
}

二进制是一个好东西,既然数据范围很小,我们可以考虑二进制存状态,把非强连通图筛出去.

先更新一下每一个点的出边的集合的交,找交集是因为我们现在要找的是非强连通图,如果找并,那么最终会把强连通图也筛出去.

这样操作
mp[0]=(1<<n)-1;
for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) mp[1<<i-1]|=(read()<<j-1);//读入
for(rll i=1;i<(1<<n);i++) mp[i]=mp[lowbit(i)]&mp[i^lowbit(i)];//更新出边集合交情况,如:11010110[100]=11010110[000]+00000000[100]
//lowbit取的是最后一个1和它后面的所有0

然后枚举每一个子集,虑对于每一个强连通的子集 ,设其中所有的点的出边集合的交为 .那么对于每一个 的子集 将被更新成不是强连通的子集.

因为前面的非强连通图已经筛出去了,而后面没筛出去的状态还会再枚举到,这样就可以保证不重不漏、不多不少.最后统计没有被筛出去的情况即可.

这样操作
for(rll i=1;i<(1<<n);i++)//S
	if(!fl[i])
		for(rll j=mp[i];j;j=(j-1)&mp[i])//R
			fl[i|j]=1;
ans=1;
for(rll i=1;i<(1<<n);i++) ans+=(!fl[i]);

C.糖果

(由于,就不打LaTeX了)
设dp[i][j][k]为当前小A选到i,小B选到j,小C还剩k个位置可选.

对于小A的选择,如果当前的数字在排列a内不如在排列b内更优,那么该数字一定会被小C拿走,加入到c排列中;否则一定是被小A拿走,那么小C的选择就少一个.对于小B也就同理啦.

所以,我们可以再给dp数组加一维,表示当前进行到哪一步.

最后,统计答案时,直接乘上3*i-1和3*i-2即可.

(证明搬的网上的)

对于第i轮,在小C选之前小A和小B一定各选了一个数,并且一定是在小C当前要选的数的优先级前选的.

举个例子就是i=3的时候此时小C应该选择第9个数,那么小A和小B选择的就是小C整个喜好序列中的前8个数,方案就有 个.

因此对于第 i 轮而言要乘上一个 .

(因为是搬的所以有LaTeX)

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 401
#define mod 1000000007
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|48);
}
ll n,a[maxn],b[maxn],ta[maxn],tb[maxn];
ll dp[maxn][maxn][201][2],ans;
int main()
{
	n=read();
	for(rll i=1;i<=n;i++) a[i]=read(),ta[a[i]]=i;
	for(rll i=1;i<=n;i++) b[i]=read(),tb[b[i]]=i;
	dp[1][1][0][0]=1;
	for(rll i=1;i<=n+1;i++)
		for(rll j=1;j<=n+1;j++)
			for(rll k=0;k<=n/3;k++)
			{
				if(dp[i][j][k][0])
				{
					if(i==n+1)
					{
						if(!k) ans=(ans+dp[i][j][k][0])%mod;continue;
					}
					if(tb[a[i]]<j) dp[i+1][j][k][0]=(dp[i+1][j][k][0]+dp[i][j][k][0])%mod;
					else
						dp[i][j][k][1]=(dp[i][j][k][1]+dp[i][j][k][0])%mod,
						dp[i+1][j][k-1][0]=(dp[i+1][j][k-1][0]+dp[i][j][k][0]*k%mod)%mod;
				}
				if(dp[i][j][k][1])
				{
					if(ta[b[j]]<i) dp[i][j+1][k][1]=(dp[i][j+1][k][1]+dp[i][j][k][1])%mod;
					else if(a[i]!=b[j])
						dp[i+1][j+1][k+1][0]=(dp[i+1][j+1][k+1][0]+dp[i][j][k][1])%mod,
						dp[i][j+1][k-1][1]=(dp[i][j+1][k-1][1]+dp[i][j][k][1]*k%mod)%mod;
				}
			}
	for(rll i=1;i<=n;i++) if(i%3) ans=ans*i%mod;
	write(ans);
	return 0;
}

D.树

先将这个树进行轻重链剖分.

对于重边,可以直接使用线段树维护颜色序列.

对于轻边,可以维护两个时间戳,一个打在父亲上(lv)表示这条边最后一次被涂黑的时间;另一个打在儿子上(rv)表示这条边最后一次被涂白的时间.同样使用线段树维护.

具体细节

点击查看代码
struct tree
{
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
	ll lv,rv,v,tag;
}t[maxn<<2];

static inline void pushup(rll rt)
{
	t[rt].v=t[ls(rt)].v+t[rs(rt)].v;
	if(t[ls(rt)].rv!=t[rs(rt)].lv)t[rt].v++;
	t[rt].lv=t[ls(rt)].lv;t[rt].rv=t[rs(rt)].rv;
	return;
}
static inline void pushdown(rll rt)
{
	if(t[rt].tag)
	{
		t[ls(rt)].lv=t[ls(rt)].rv=t[rs(rt)].lv=t[rs(rt)].rv=t[rt].tag;
		t[ls(rt)].tag=t[rs(rt)].tag=t[rt].tag;
		t[ls(rt)].v=t[rs(rt)].v=0;
		t[rt].tag=0;
	}
}
static inline void build(rll rt,rll l,rll r)
{
	if(l==r)
	{
		t[rt].lv=t[rt].rv=l;
		t[rt].v=0;
		return;
	}
	rll mid=(l+r)>>1;
	build(ls(rt),l,mid);
	build(rs(rt),mid+1,r);
	pushup(rt);
}
static inline void upd(rll rt,rll l,rll r,rll x,rll y,rll v)
{
	if(x<=l&&r<=y) { t[rt].v=0;t[rt].tag=t[rt].lv=t[rt].rv=v;return; }
	pushdown(rt);
	rll mid=(l+r)>>1;
	if(x<=mid)upd(ls(rt),l,mid,x,y,v);
	if(y>mid)upd(rs(rt),mid+1,r,x,y,v);
	pushup(rt);
}
static inline ll query(rll rt,rll l,rll r,rll x,rll y)
{
	if(x<=l&&r<=y) return t[rt].v;
	pushdown(rt);
	rll mid=(l+r)>>1,ans=0;
	if(x<=mid) ans+=query(ls(rt),l,mid,x,y);
	if(y>mid) ans+=query(rs(rt),mid+1,r,x,y);
	if(x<=mid&&y>mid&&t[ls(rt)].rv!=t[rs(rt)].lv) ans++;
	return ans;
}

static inline ll chk(rll rt,rll l,rll r,rll pos)
{
	if(l==r) return t[rt].lv;
	pushdown(rt);
	rll mid=(l+r)>>1;
	if(pos<=mid)return chk(ls(rt),l,mid,pos);
	else return chk(rs(rt),mid+1,r,pos);
}
static inline void update(rll x,rll y)
{
	cnt++;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		upd(1,1,n,dfn[top[x]],dfn[x],cnt);
		x=f[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	upd(1,1,n,dfn[y],dfn[x],cnt);
}
static inline ll Query(rll x,rll y)
{
	rll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		ans+=query(1,1,n,dfn[top[x]],dfn[x]);
		if(chk(1,1,n,dfn[top[x]])!=chk(1,1,n,dfn[f[top[x]]]))ans++;
		x=f[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	ans+=query(1,1,n,dfn[y],dfn[x]);
	return ans;
}

while(q--)
{
	op=read();rll x=read(),y=read();
	if(op==1) update(x,y);else write(Query(x,y)),puts("");
}
posted @   1Liu  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示