Codeforces Forethought Future Cup Elimination Round 选做

Codeforces Forethought Future Cup Elimination Round 选做

1146C Tree Diameter

题意

交互题。有一棵 \(n(n\le 100)\) 个点的树,你可以进行不超过 \(9\) 次询问,每次询问两个点集中两个不在同一点集的点的最大距离。求树的直径。

题解

GXOI2019旅行者 基本类似,二进制分组,对于每一位,编号当前位为 \(0\) 的分到一组,当前位为 \(1\) 的分到另一组。最大询问次数为 \(\log 100 = 7\)

code

#include<cstdio>
int v1[105],v2[105];
int main()
{
	int T; scanf("%d",&T);
	while(T--)
	{
		int n,ans=0; scanf("%d",&n);
		for(int i=0;i<=6;++i)
		{
			int id1=0,id2=0;
			for(int j=1;j<=n;++j) (j&(1<<i))?v1[++id1]=j:v2[++id2]=j;
			if(id1&&id2)
			{
				printf("%d %d ",id1,id2);
				for(int i=1;i<=id1;++i) printf("%d ",v1[i]);
				for(int i=1;i<=id2;++i) printf("%d ",v2[i]);
				puts(""); fflush(stdout);
				int x; scanf("%d",&x);
				if(x>ans) ans=x;
			}
		}
		printf("-1 %d\n",ans);
		fflush(stdout);
	}
}

1146D Frog Jumping

题意

有一条数轴,一只青蛙在原点,可以向前跳 \(a\) 步或向后跳 \(b\) 步。

定义 \(f(x)\) 表示青蛙在 \([0,x]\) 里跳,能跳到的点数。

\(\sum_{i=0}^m f(i)\)\(m\le 10^9, a,b\le 10^5\)

题解

能到达的点 \(c\) 能被表示为 \(ax-by=c\)

根据裴蜀定理,能到达的点一定是 \(\gcd(a,b)\) 的倍数。

但是,当 \(i<a+b\) 时,\(f(i)\) 由于跳的点不能超过 \(i\) ,有的点可能会无法跳到。故 \(f(0)\sim f(a+b)\) 的答案我们要另外计算。

我们考虑贪心的去跳,当跳的步数 \(>b\) 就减去 \(b\) 。暴力枚举 \(i\) 统计有多少个点能到达即可。

注意不要重复统计答案。

code

#include<cstdio>
int gcd(int x, int y) {
	return y?gcd(y,x%y):x;
}
const int N=2e5+5;
bool vis[N];
int step[N],tot;
int main()
{
#ifndef ONLINE_JUDGE
	freopen("sol.in","r",stdin);
#endif
	int m,a,b;
	scanf("%d%d%d",&m,&a,&b);
	const int g=gcd(a,b);
	long long ans=1ll*(1+m/g+1)*(m/g+1)/2ll*g-1ll*(1ll*(m/g+1)*g-m-1)*(m/g+1);
	vis[step[++tot]=0]=true;
	while(true)
	{
		++tot;
		step[tot]=step[tot-1]>=b?step[tot-1]-b:step[tot-1]+a;
		if(vis[step[tot]]) break;
		vis[step[tot]]=true;
	} --tot;
	for(int i=0,j=1;i<a+b&&i<=m;++i)
	{
		ans-=i/g+1;
		while(step[j]<=i&&j<=tot) ++j;
		ans+=j-1;
	}
	printf("%lld",ans);
}

1146E Hot is Cold

题意

给你一个长度为 \(n\) 的序列和 \(q\) 个操作,每次操作将 \(<x_i\) 的取反或 \(>x_i\) 的数取反。求最后的序列。\(n,q\le 10^5\)

题解

对正数和负数分别维护权值线段树,然后分类讨论维护每个数是否被取反的标记。

考虑 \(>x\) :当 \(x<0\) :将绝对值 \(>x\) 的数置为负 ; 当 \(x>0\) ,将正负数绝对值 \(<-x\) 标记取反,绝对值 \(\ge -x\) 的数置为负;

\(<x\) 与上述类似。

有神仙线性做法,待补充……

code

#include<cstdio>
#include<cstring>
inline int gi()
{
	char c=getchar(); int x=0,f=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
	for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
	return x*f;
}
const int N=1e5+5;
int st[2][N<<2],tg[2][N<<2],rev[2][N<<2],fg[2][N<<2],a[N],n,q,m;
#define lx (x<<1)
#define rx (x<<1|1)
void pushdown(int i, int x)
{
	if(tg[i][x]!=-1)
	{
		tg[i][lx]=tg[i][rx]=st[i][lx]=st[i][rx]=tg[i][x];
		tg[i][x]=-1;
	}
	if(fg[i][x])
	{
		fg[i][lx]=fg[i][rx]=1;
		rev[i][lx]=rev[i][rx]=0;
		fg[i][x]=0;
	}
	if(rev[i][x])
	{
		rev[i][lx]^=1,rev[i][rx]^=1;
		st[i][lx]^=1,st[i][rx]^=1;
		rev[i][x]=0;
	}
}
void rever(int i, int x, int l, int r, int sl, int sr)
{
	if(sl>sr) return ;
	if(sl<=l&&r<=sr) {
		rev[i][x]^=1,st[i][x]^=1; return ;
	}
	pushdown(i,x);
	int mid=l+r>>1;
	if(sl<=mid) rever(i,lx,l,mid,sl,sr);
	if(sr>mid) rever(i,rx,mid+1,r,sl,sr);
}
void update(int i, int x, int l, int r, int sl, int sr, int w)
{
	if(sl>sr) return ;
	if(sl<=l&&r<=sr)
	{
		tg[i][x]=st[i][x]=w, rev[i][x]=0, fg[i][x]=1;
		return ;
	}
	pushdown(i,x);
	int mid=l+r>>1;
	if(sl<=mid) update(i,lx,l,mid,sl,sr,w);
	if(sr>mid) update(i,rx,mid+1,r,sl,sr,w);
}
int qry(int i, int x, int l, int r, int s)
{
	if(l==r) return st[i][x];
	pushdown(i,x);
	int mid=l+r>>1;
	return (s<=mid?qry(i,lx,l,mid,s):qry(i,rx,mid+1,r,s));
}
int main()
{
	n=gi(),q=gi();
	for(int i=1;i<=n;++i)
	{
		a[i]=gi();
		if(a[i]>m) m=a[i];
		if(-a[i]>m) m=-a[i];
	}
	memset(tg,-1,sizeof(tg));
	while(q--)
	{
		char s[2]; scanf("%s",s);
		int x=gi();
		if(s[0]=='>')
		{
			if(x>0)
			{
				update(0,1,1,m,x+1,m,0);
				update(1,1,1,m,x+1,m,1);
			}
			else
			{
				rever(0,1,1,m,1,-x-1);
				update(0,1,1,m,-x,m,0);
				rever(1,1,1,m,1,-x-1);
				update(1,1,1,m,-x,m,1);
			}
		}
		else
		{
			if(x<0)
			{
				update(0,1,1,m,-x+1,m,1);
				update(1,1,1,m,-x+1,m,0);
			}
			else
			{
				rever(0,1,1,m,1,x-1);
				update(0,1,1,m,x,m,1);
				rever(1,1,1,m,1,x-1);
				update(1,1,1,m,x,m,0);
			}
		}
	}
	for(int i=1;i<=n;++i)
	{
		if(a[i]==0) printf("0 ");
		else if(a[i]<0) printf("%d ",a[i]*(qry(0,1,1,m,-a[i])?-1:1));
		else printf("%d ",a[i]*(qry(1,1,1,m,a[i])?-1:1));
	}
}

1146F Leaf Partition

题意

给你一棵 \(n\) 个节点的树,将叶子节点划分成若干个集合,使得每个集合的虚树互不相交。求划分方案数。

\(n\le 2\cdot 10^5\)

题解

\(f(u,0)\) 表示 \(u\) 不属于任何集合的方案数。

\(f(u,1)\) 表示 \(u\) 连通了 \(1\) 个子节点。显然,当前节点必须要连通到上面的集合。

\(f(u,2)\) 表示 \(u\) 连通了 \(2\) 个以上的子节点。

这样,对于子节点 \(v\) ,将其分到不同集合的方案数为 \(f(v,0)+f(v,2)\) ,设为 \(x\) ;将其分到同一集合的方案数为 \(f(v,1)+f(v,2)\) ,设为 \(y\)

这样就不难得出转移:

\[\begin{cases} f'(u,0)=f(u,0)\times x\\ f'(u,1)=f(u,0)\times y+f(u,1)\times x \\ f'(u,2)=f(u,1)\times y+f(u,2)\times (x+y) \end{cases} \]

code

#include<cstdio>
inline int gi()
{
	char c; int x=0;
	for(;c<'0'||c>'9';c=getchar());
	for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
	return x;
}
const int N=200005,Mod=998244353;
int f[N][3],head[N],nxt[N],to[N],n;
void addedge(int u, int v, int now) {
	nxt[now]=head[u], head[u]=now, to[now]=v;
}
#define mul(x,y) (1ll*x*y%Mod)
void dfs(int u)
{
	if(!head[u]) f[u][2]=1;
	else f[u][0]=1;
	for(int e=head[u];e;e=nxt[e])
	{
		int& v=to[e];
		dfs(v);
		int x=(f[v][0]+f[v][2])%Mod,y=(f[v][1]+f[v][2])%Mod;
		int f0=mul(f[u][0],x);
		int f1=(mul(f[u][0],y)+mul(f[u][1],x))%Mod;
		int f2=(mul(f[u][1],y)+mul(f[u][2],(x+y)%Mod))%Mod;
		f[u][0]=f0,f[u][1]=f1,f[u][2]=f2;
	}
}
int main()
{
	n=gi();
	for(int i=2;i<=n;++i)
		addedge(gi(),i,i-1);
	dfs(1);
	printf("%d",(f[1][0]+f[1][2])%Mod);
}

posted @ 2019-04-22 20:11  x_faraway_x  阅读(407)  评论(4编辑  收藏  举报