8.19 模拟赛小结

前言

结束了 这篇写简单一点吧

T1 颠倒黑白

image
很强的构造题
根据打表找出思路
因为最左下角的是一定要点的 就考虑它

  • 如果是先手 左下角有黑色 就把它点了 后手只能帮我们把其它黑色点了 最后还是我们先点完
  • 若是后手 左下角是白色 与先手同理

一个简单判断即可

#include<bits/stdc++.h>
using namespace std;
int g,n,m,c;
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>g;
	while(g--)
	{
		cin>>n>>m;
		for(int i=1;i<=n-1;i++)
			for(int j=1;j<=m;j++)
				cin>>c;
		cin>>c;
		if(c) cout<<"T\n";
		else cout<<"F\n";
		for(int j=2;j<=m;j++)
			cin>>c;
	}
	return 0;
}

T2 糖果数 原题

题意:给定一个 \(n\space (n\leq 10^{18})\) 求在 \(n\) 位数以内有多少个包含 \(111\) 的数
推荐我的博客:矩阵快速幂
这题就是原题换了一下 不想说了 容斥一下转为求没有 \(111\) 的数即可

Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll mod=998244353;
ll n;
struct node{
	ll r[4][4];
	int n,m;
}ans,a;
node clear(int n,int m)
{
	node c;
	c.n=n,c.m=m;
	memset(c.r,0,sizeof(c.r));
	return c;
}
node operator *(node a,node b)
{
	node c=clear(a.n,b.m);
	for(int i=1;i<=a.n;i++)
		for(int k=1;k<=a.m;k++)
			for(int j=1;j<=b.m;j++)
				c.r[i][j]=(c.r[i][j]+a.r[i][k]*b.r[k][j])%mod;
	return c;
}
ll Qpow(ll a,ll x)
{
	ll sum=1;
	while(x)
	{
		if(x&1) sum=sum*a%mod;
		a=a*a%mod;
		x/=2;
	}
	return sum;
}
node Qpow(node a,ll x)
{
	node sum=clear(a.m,a.m);
	for(int i=1;i<=a.m;i++)
		sum.r[i][i]=1;
	while(x)
	{
		if(x&1) sum=sum*a;
		a=a*a;
		x/=2;
	}
	return sum;
}
int main()
{
	scanf("%lld",&n);
	ans.n=1,ans.m=3;
	ans.r[1][1]=9,ans.r[1][2]=1,ans.r[1][3]=0;
	a.n=a.m=3;
	a.r[1][1]=9,a.r[1][2]=1,a.r[1][3]=0;
	a.r[2][1]=9,a.r[2][2]=0,a.r[2][3]=1;
	a.r[3][1]=9,a.r[3][2]=0,a.r[3][3]=0;	
	ans=ans*Qpow(a,n-1);
	printf("%lld",(Qpow(10,n)-(ans.r[1][1]+ans.r[1][2]+ans.r[1][3])%mod+mod)%mod);
	return 0;
}

T3 天使玩偶 原题

题意:会动态加点 每次询问一个点离另一个点最小的曼哈顿距离
有很多情况 考虑 \(b_x\leq a_x\space b_y\leq a_y\) 的情况 即 \(b\)\(a\) 左下角
那么问题转化为 求
\(a_{tim}\leq b_{tim},{a_x\leq b_x,a_y\leq b_y}\)
现在的问题转化为求 \(a_x-b_x+a_y-b_y\) 最小
移一下就是 \(a_x+a_y-b_x-b_y\)
树状数组位数 \(b_x+b_y\) 然后 CDQ 分治即可
考虑在左上的点
直接将整个矩阵翻转一下即可 可以用坐标轴理解
时间复杂度太大了 会 TLE
所以 CDQ 的 sort 不能用 用归并排序就可以了
不开 O2 能过

#include<bits/stdc++.h>
//#pragma GCC optimize (1)
//#pragma GCC optimize (2)
//#pragma GCC optimize (3,"Ofast","inline")
#define reg register
#define N 300005
#define M 1000005
using namespace std;
int n,m,ans[N*2],maxy;
struct oper{
	int opr,x,y,id;
}q[N*2],b[N*2],t[N*2];
inline bool cmp(oper a,oper b)
{
	return a.x<b.x; 
}
struct BIT{
	int tr[M];
	inline void add(int x,int v)
	{
		while(x<=maxy)
		{
			tr[x]=max(tr[x],v);
			x+=x&-x;
		}
		return;
	} 
	inline int ask(int x)
	{
		int maxx=-1e9;
		while(x)
		{
			maxx=max(maxx,tr[x]);
			x-=x&-x;
		}
		return maxx;
	}
	inline void clear(int x)
	{
		while(x<=maxy)
		{
			tr[x]=-1e9;
			x+=x&-x; 
		}
	}
}tr;
int stk[4*N],top;
inline void solve(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)/2;
	solve(l,mid);
	solve(mid+1,r);
	int i=l,j=mid+1,len;
	i=l,j=mid+1;
	for(j=mid+1;j<=r;j++)
	{
		while(i<=mid&&q[i].x<=q[j].x)
		{
			if(q[i].opr==2)
			{
				++i;
				continue;
			}
			tr.add(q[i].y,q[i].x+q[i].y);
			stk[++top]=q[i].y;
			++i;
		}
		if(q[j].opr==2)
			ans[q[j].id]=min(ans[q[j].id],q[j].x+q[j].y-tr.ask(q[j].y));
	}
	while(top) tr.clear(stk[top--]);
	i=l,j=mid+1,len=l-1;
	for(i=l;i<=r;i++)
		t[i]=q[i];
	for(i=l;i<=mid;i++)
	{
		while(j<=r&&cmp(t[j],t[i])) q[++len]=t[j],j++;
		q[++len]=t[i];
	}
	while(j<=r) q[++len]=t[j],j++;
	
}
int main()
{
	scanf("%d%d",&n,&m);
	for(reg int i=1;i<=n;i++)
	{
		q[i].id=i;
		q[i].opr=1;
		scanf("%d%d",&q[i].x,&q[i].y);
		q[i].x++,q[i].y++;
		maxy=max(maxy,q[i].y);
	}
	for(reg int i=n+1;i<=n+m;i++)
		scanf("%d%d%d",&q[i].opr,&q[i].x,&q[i].y),q[i].id=i,
		ans[i]=2e9,q[i].x++,q[i].y++,maxy=max(maxy,q[i].y);
	maxy++;
	fill(tr.tr,tr.tr+1+M-3,-1e9);
	for(reg int i=1;i<=n+m;i++) b[i]=q[i];
	solve(1,n+m);
	for(reg int i=1;i<=n+m;i++)
		q[i]=b[i],q[i].x=M-q[i].x;
	solve(1,n+m);
	for(reg int i=1;i<=n+m;i++)
		q[i]=b[i],q[i].y=maxy-q[i].y;
	solve(1,n+m);
	for(reg int i=1;i<=n+m;i++)
		q[i]=b[i],q[i].x=M-q[i].x,q[i].y=maxy-q[i].y;
	solve(1,n+m);
	for(reg int i=1;i<=n+m;i++)	q[i]=b[i];
	for(reg int i=n+1;i<=m+n;i++)
		if(q[i].opr==2) printf("%d\n",ans[i]);
	return 0;
}

T4 数学题 原题

image
image

可以先去做一下这题

发现删边很难 考虑加边
可以先把原本存在的边缩点成树
然后如果连上两个点可以发现这两个点之间的边都不是割边 因为已经变成一个环了
所以这两点之间的路径全部都打上不是割边的记录
树剖维护即可

#include<bits/stdc++.h>
#define N 100005
#define mp make_pair
using namespace std;
int n,m,q;
struct oper{
	int u,v,opr;
}op[N];
int u[N],v[N];
map <pair<int,int> ,int> use; 
//Edge
int head[N],tot=1;
struct edge{
	int to,next;
}e[N*2];
void add(int u,int v)
{
	e[tot]=(edge){v,head[u]};
	head[u]=tot++;
}
//Tarjan
int stk[N],l;
int col[N],dfn[N],low[N],colcnt,ids;
void tarjan(int x,int fa)
{
	low[x]=dfn[x]=++ids;
	stk[++l]=x;
	for(int i=head[x];i;i=e[i].next)
	{
		int to=e[i].to;
		if(to==fa) continue;
		if(!dfn[to]) tarjan(to,x);
		low[x]=min(low[x],low[to]);
	}
	if(dfn[x]==low[x])
	{
		colcnt++;
		while(stk[l+1]!=x)
			col[stk[l--]]=colcnt;
	}
}
//线段树
int tr[N*4],lz[N*4]; 
void Pushup(int x)
{
	tr[x]=tr[x*2]+tr[x*2+1];
}
void Pushdown(int x,int l)
{
	if(lz[x]==-1) return;
	tr[x*2]=(l-l/2)*lz[x];
	lz[x*2]=lz[x];
	tr[x*2+1]=(l/2)*lz[x];
	lz[x*2+1]=lz[x];
	lz[x]=-1;	
}
void updata(int l,int r,int L,int R,int x,int v)
{
	if(l>R||r<L) return;
	if(l>=L&&r<=R) 
	{
		tr[x]=(r-l+1)*v;
		lz[x]=v;
		return;
	}
	Pushdown(x,r-l+1);
	int mid=(l+r)/2;
	updata(l,mid,L,R,x*2,v);
	updata(mid+1,r,L,R,x*2+1,v);
	Pushup(x);
}
int query(int l,int r,int L,int R,int x)
{
	if(l>R||r<L) return 0;
	if(l>=L&&r<=R) return tr[x];
	int mid=(l+r)/2;
	Pushdown(x,r-l+1);
	return query(l,mid,L,R,x*2)+query(mid+1,r,L,R,x*2+1);
}
//树剖
int fa[N],dep[N],size[N],Son[N]; 
int cnt,id[N],top[N];
void dfs1(int now,int f)
{
	fa[now]=f;
	dep[now]=dep[f]+1;
	int maxx=0;
	size[now]=1;
	for(int i=head[now];i;i=e[i].next)
	{
		int son=e[i].to;
		if(son==f) continue;
		dfs1(son,now);
		size[now]+=size[son];
		if(size[son]>maxx) maxx=size[son],Son[now]=son;
	}
}
void dfs2(int now,int topf)
{
	id[now]=++cnt;
	top[now]=topf;
	if(!Son[now]) return;
	dfs2(Son[now],topf);
	for(int i=head[now];i;i=e[i].next)
	{
		int son=e[i].to;
		if(son==Son[now]||son==fa[now]) continue;
		dfs2(son,son);
	}
}
void Add(int u,int v)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		updata(1,colcnt,id[top[u]],id[u],1,0);
		u=fa[top[u]];
	}
	if(u==v) return;
	if(dep[u]>dep[v]) swap(u,v);
	updata(1,colcnt,id[u]+1,id[v],1,0);
	return;
}
int Ask(int u,int v)
{
	int sum=0;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		sum+=query(1,colcnt,id[top[u]],id[u],1);
		u=fa[top[u]];
	}
	if(u==v) return sum; 
	if(dep[u]>dep[v]) swap(u,v);
	sum+=query(1,colcnt,id[u]+1,id[v],1);
	return sum; 
}
// end
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d%d",&u[i],&v[i]),use[mp(u[i],v[i])]=use[mp(v[i],u[i])]=1;
	for(q=1;;q++)
	{
		scanf("%d",&op[q].opr);
		if(op[q].opr==-1)
		{
			q--;
			break;
		}
		scanf("%d%d",&op[q].u,&op[q].v);
		if(op[q].opr==0) use[mp(op[q].u,op[q].v)]=use[mp(op[q].v,op[q].u)]=0;
	}
	for(int i=1;i<=m;i++)
		if(use[mp(u[i],v[i])]) add(u[i],v[i]),add(v[i],u[i]);
	for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i,0);
	//缩点 
	l=0;
	tot=1;
	memset(head,0,sizeof(head));
	memset(lz,-1,sizeof(lz));
	for(int i=1;i<=m;i++)
		if(use[mp(u[i],v[i])]&&col[u[i]]!=col[v[i]])
			add(col[u[i]],col[v[i]]),
			add(col[v[i]],col[u[i]]);
	//
	dfs1(1,0);
	dfs2(1,1);
	updata(1,colcnt,2,colcnt,1,1);
	for(int i=q;i>=1;i--)
		if(op[i].opr==0)
			Add(col[op[i].u],col[op[i].v]);
		else
			stk[++l]=Ask(col[op[i].u],col[op[i].v]);
	while(l)
		printf("%d\n",stk[l--]);
	return 0;
}

写的比较草 没什么好的

posted @ 2023-08-19 16:12  g1ove  阅读(9)  评论(0编辑  收藏  举报