【CF1146】Forethought Future Cup - Elimination Round

Forethought Future Cup - Elimination Round

窝也不知道这是个啥比赛QwQ

A. Love "A"

给你一个串,你可以删去若干个元素,使得最后a的个数严格大于一半。求最大串长。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char s[55];int v=0,n;
int main()
{
	scanf("%s",s+1);n=strlen(s+1);
	for(int i=1;i<=n;++i)v+=(s[i]=='a');
	while(v*2<=n)--n;
	printf("%d\n",n);
	return 0;
}

B. Hate "A"

给你一个串\(s\),定义\(s'\)\(s\)删去所有a后的串,定义\(t=s+s'\)
现在给你\(t\),你需要还原一组合法的\(s\)

找到极大位置满足前面删去a之后的串长和后面相等,判断一下两个串是否相等就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 100100
char s[MAX],a[MAX];
int n,t;
void check(int p)
{
	for(int i=p;i<=n;++i)
		if(s[i]!=a[i-p+1])return;
	for(int i=1;i<p;++i)putchar(s[i]);
	puts("");exit(0);
}
int main()
{
	scanf("%s",s+1);n=strlen(s+1);
	for(int i=1;i<=n;++i)
	{
		if(s[i]!='a')a[++t]=s[i];
		if(n-i==t)check(i+1);
	}
	puts(":(");
	return 0;
}

C. Tree Diameter

交互题。
有一棵树,每次你可以询问两个集合,会告诉你从这两个集合中各取一个点的最大距离。现在你要求出树的直径。
询问次数不超过\(9\)次。
\(n\le 100\),边权不超过\(100\)

我们只需要确定直径的一个端点,然后询问这个点到其他所有点的距离的最大值就行了。
一个很呆的想法是我们每次随机把点集分成两半,然后求一个最大值,但是这样子的正确率是\(\frac{1}{512}\),显然是不够的。
我们知道这样一个结论:距离树上某个点的最远点一定是直径的一个端点。
那么我们先对于\(1\)求出到其他所有点的最远距离,这样子对于剩余的点集进行二分就可以求出一个直径的端点,再询问一次就是答案啦。

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
int Query(vector<int> &A,vector<int> &B)
{
	printf("%d %d ",A.size(),B.size());
	for(int v:A)printf("%d ",v);
	for(int v:B)printf("%d ",v);
	puts("");fflush(stdout);
	int x;scanf("%d",&x);
	return x;
}
int main()
{
	int T,n;scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		vector<int> A,B;
		A.push_back(1);
		for(int i=2;i<=n;++i)B.push_back(i);
		int now=Query(A,B);
		int l=0,r=n-2;
		while(l<r)
		{
			int mid=(l+r)>>1;
			vector<int> a,b;
			for(int i=0;i<=mid;++i)a.push_back(B[i]);
			for(int i=mid+1;i<n-1;++i)b.push_back(B[i]);
			if(Query(A,a)==now)r=mid;
			else l=mid+1;
		}
		int p=B[l];
		A.clear();B.clear();
		A.push_back(p);
		for(int i=1;i<=n;++i)if(i!=p)B.push_back(i);
		printf("-1 %d\n",Query(A,B));fflush(stdout);
	}
	return 0;
}

D. Frog Jumping

有一只青蛙,初始时在\(0\)位置,每次可以沿数轴正方向跳\(a\)个单位或者是沿着负方向跳\(b\)个单位。
定义\(f(x)\)为在不跳出区间\([0,x]\)的情况下,能够跳到的位置个数。
\(\sum_{i=0}^m f(i)\)
\(a,b\le 10^5,m\le 10^9\)

先来看看\(f(n)\)怎么求,大力猜想一下一定能够从\(f(n-a)\)转移过来,然后再考虑一下中间这\(a\)步能够走到些什么地方,大力猜猜可以走到所有\((n-a)+gcd(a,b)\)倍数的位置,所以大力猜想一下\(f(n+a)=f(n)+\frac{a}{gcd(a,b)}\)
然而这样子在\(n\)很小的时候肯定是假的,估摸一下\(n\ge a+b\)的时候是真的,其他时候是假的。
但是\(a+b\le 2*10^5\),所以我们可以考虑大力暴力计算一下答案。
怎么算呢?设\(f[i]\)表示经过不超过最小的\(f[i]\)可以到达\(i\)位置。这个东西显然就是一个最短路的问题,跑一遍\(dijkstra\)就可以算出来。
剩下的就是一个简单的计算问题啦。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define pi pair<ll,int>
#define mp make_pair
#define MAX 1000000
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
ll dis[MAX],f[MAX];
bool vis[MAX];
int m,a,b,N;
priority_queue<pi,vector<pi>,greater<pi> >Q;
int main()
{
	m=read();a=read();b=read();N=a+a+b;
	memset(dis,63,sizeof(dis));
	dis[0]=0;Q.push(mp(0,0));
	while(!Q.empty())
	{
		int u=Q.top().second;Q.pop();
		if(vis[u])continue;vis[u]=true;
		if(u>=b&&dis[u-b]>dis[u])dis[u-b]=dis[u],Q.push(mp(dis[u-b],u-b));
		if(u+a<=N&&dis[u+a]>max(0ll+u+a,dis[u]))dis[u+a]=max(0ll+u+a,dis[u]),Q.push(mp(dis[u+a],u+a));
	}
	ll ans=0;int mm=min(m,a+b-1);
	for(int i=0;i<=N;++i)
		if(dis[i]<=N)f[dis[i]]+=1;
	for(int i=1;i<=N;++i)f[i]+=f[i-1];
	for(int i=0;i<=mm;++i)ans+=f[i];
	if(m>=a+b)
	{
		int d=a/__gcd(a,b);
		for(int i=a+b,j=0;j<a&&i<=m;++j,++i)
		{
			int L=(m-i)/a;
			ans+=(L+1)*f[i]+1ll*L*(L+1)/2*d;
		}
	}
	printf("%lld\n",ans);
	return 0;
}

E. Hot is Cold

一开始有一个数列,你要执行\(n\)次操作,每次给你一个\(>x\)或者\(<x\),如果一个数带进去合法,就把他变成相反数,求出最后的数列。
\(n,Q\le 2*10^5,-10^5\le a_i\le 10^5\)

显然只需要对于每个值算答案最后再询问就好了。
那么拿一个线段树维护值域,记录每个值是否被翻转过。
那么每次大力讨论,发现只需要维护区间赋值和区间翻转就行了。
注意这里是\(<\)\(>\),所以要特殊处理一下\(x\)\(-x\)这两个位置。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 200200
#define lson (now<<1)
#define rson (now<<1|1)
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int t[MAX<<2],tag[MAX<<2],rev[MAX<<2];
void puttag(int now,int v){t[now]=tag[now]=v;rev[now]=0;}
void putrev(int now){t[now]^=1;rev[now]^=1;}
void pushdown(int now)
{
	if(~tag[now])
	{
		puttag(lson,tag[now]);
		puttag(rson,tag[now]);
		tag[now]=-1;
	}
	if(rev[now])
	{
		putrev(lson);putrev(rson);
		rev[now]=0;
	}
}
void Modify(int now,int l,int r,int L,int R,int w)
{
	if(L>R)return;
	if(L<=l&&r<=R){puttag(now,w);return;}
	int mid=(l+r)>>1;pushdown(now);
	if(L<=mid)Modify(lson,l,mid,L,R,w);
	if(R>mid)Modify(rson,mid+1,r,L,R,w);
}
void Modify(int now,int l,int r,int L,int R)
{
	if(L>R)return;
	if(L<=l&&r<=R){putrev(now);return;}
	int mid=(l+r)>>1;pushdown(now);
	if(L<=mid)Modify(lson,l,mid,L,R);
	if(R>mid)Modify(rson,mid+1,r,L,R);
}
int Query(int now,int l,int r,int p)
{
	if(l==r)return t[now];
	int mid=(l+r)>>1;pushdown(now);
	if(p<=mid)return Query(lson,l,mid,p);
	else return Query(rson,mid+1,r,p);
}
int n,Q,x,a[MAX];char ch[MAX];
const int N=1e5;
int main()
{
	n=read();Q=read();
	for(int i=1;i<=n;++i)a[i]=read();
	memset(tag,-1,sizeof(tag));
	while(Q--)
	{
		scanf("%s",ch);x=read();
		if(ch[0]=='>')
		{
			if(x>=0)Modify(1,-N,N,x+1,N,1),Modify(1,-N,N,-N,-x-1,0);
			else
			{
				Modify(1,-N,N,x+1,-x-1),Modify(1,-N,N,-x+1,N,1),Modify(1,-N,N,-N,x-1,0);
				if(!Query(1,-N,N,-x))Modify(1,-N,N,-x,-x);
				if(Query(1,-N,N,x))Modify(1,-N,N,x,x);
			}
		}
		else
		{
			if(x<=0)Modify(1,-N,N,-N,x-1,1),Modify(1,-N,N,-x+1,N,0);
			else
			{
				Modify(1,-N,N,-x+1,x-1),Modify(1,-N,N,-N,-x-1,1),Modify(1,-N,N,x+1,N,0);
				if(!Query(1,-N,N,-x))Modify(1,-N,N,-x,-x);
				if(Query(1,-N,N,x))Modify(1,-N,N,x,x);
			}
		}
	}
	for(int i=1;i<=n;++i)printf("%d ",Query(1,-N,N,a[i])?-a[i]:a[i]);
	puts("");return 0;
}

F. Leaf Partition

给你一棵树,你要把叶子节点分成若干个集合,使得每个集合的\(f(S)\)都无交集。\(f(S)\)为最小的连通图使得所有\(S\)中的叶子节点都被连通。
求划分的方案数。
\(n\le 2*10^5\)

\(f[i][0/1/2]\)表示当前考虑以\(i\)为根的子树,\(0\)表示当前点不会被划分到任何一个集合中,\(1\)表示和\(1\)个儿子相连,\(2\)表示和两个以上的儿子相连,转移就很简单了。

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 200200
#define MOD 998244353
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,f[MAX][3];
vector<int> E[MAX];
void dfs(int u)
{
	if(E[u].empty())f[u][2]=1;else f[u][0]=1;
	for(int v:E[u])
	{
		dfs(v);
		f[u][2]=(1ll*f[u][2]*(f[v][0]+f[v][2])+1ll*(f[u][1]+f[u][2])*(f[v][1]+f[v][2]))%MOD;
		f[u][1]=(1ll*f[u][1]*(f[v][0]+f[v][2])+1ll*f[u][0]*(f[v][1]+f[v][2]))%MOD;
		f[u][0]=1ll*f[u][0]*(f[v][0]+f[v][2])%MOD;
	}
}
int main()
{
	n=read();
	for(int i=2;i<=n;++i)E[read()].push_back(i);
	dfs(1);printf("%d\n",(f[1][0]+f[1][2])%MOD);
	return 0;	
}

G. Zoning Restrictions

一个人要在一条路上修\(n\)栋高度为\([0,h]\)的房子,假设修了一栋高度为\(a\)的房子就会产生收益\(a^2\)。有\(m\)个限制,每个限制\([l,r],x,c\),即在\([l,r]\)这些房子中,如果最高的房子严格大于了\(x\),就要交\(c\)的罚款。
求最大收益。
\(n,h,m\le 50\)

简单网络流?
先把每个点拆成\(h+1\)个点,然后串起来,连边为选择\(a\)时减少的代价。
对于一个限制\([l,r]\),把对应的第\(x+1\)个点和一个新点连起来,边权为\(inf\),再把新点和\(T\)相连,代价为罚款的值。
然后\(n*h*h\)减去最大流就是答案了。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
#define MAX 3000
const int inf=1e9;
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Line{int v,next,w;}e[1000000];
int h[MAX],cnt=2;
inline void Add(int u,int v,int w)
{
	e[cnt]=(Line){v,h[u],w};h[u]=cnt++;
	e[cnt]=(Line){u,h[v],0};h[v]=cnt++;
}
int S,T;
int level[MAX];
bool bfs()
{
	for(int i=S;i<=T;++i)level[i]=0;
	queue<int> Q;Q.push(S);level[S]=1;
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();
		for(int i=h[u];i;i=e[i].next)
			if(!level[e[i].v]&&e[i].w)
				level[e[i].v]=level[u]+1,Q.push(e[i].v);
	}
	return level[T];
}
int dfs(int u,int flow)
{
	if(u==T||!flow)return flow;
	int ret=0;
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v,d;
		if(level[v]==level[u]+1)
		{
			d=dfs(v,min(flow,e[i].w));
			ret+=d;flow-=d;
			e[i].w-=d;e[i^1].w+=d;
		}
	}
	return ret;
}
int Dinic()
{
	int ret=0;
	while(bfs())ret+=dfs(S,inf);
	return ret;
}
int a[55][55],tot,ans;
int n,H,m;
int main()
{
	n=read();H=read();m=read();
	S=0;T=n*(H+1)+m+1;ans=n*H*H;
	for(int i=1;i<=n;++i)
		for(int j=0;j<=H;++j)a[i][j]=++tot;
	for(int i=1;i<=n;++i)Add(S,a[i][0],inf);
	for(int i=1;i<=n;++i)
		for(int j=0;j<H;++j)
			Add(a[i][j],a[i][j+1],H*H-j*j);
	while(m--)
	{
		int l=read(),r=read(),x=read(),c=read();
		if(x==H)continue;++x;++tot;
		for(int i=l;i<=r;++i)Add(a[i][x],tot,inf);
		Add(tot,T,c);
	}
	printf("%d\n",ans-Dinic());
	return 0;
}

H. Satanic Panic

平面上有一堆点,你要求五角星的数量,保证不存在三点共线。
\(n\le 300\)

本质上就是求五个点的凸包数量,也就是要找五条极角序递增的边。
把所有边按照极角序排序。
\(f[i][j][1..5]\)表示从\(i\)点出发,当前到了\(j\),且中间确定了\(1..5\)条边的方案数。
每次新增一条边进来转移。
时间复杂度\(O(n^3)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 330
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Node{int x,y;}p[MAX];
Node operator-(Node a,Node b){return (Node){a.x-b.x,a.y-b.y};}
Node operator+(Node a,Node b){return (Node){a.x+b.x,a.y+b.y};}
ll Cross(Node a,Node b){return 1ll*a.x*b.y-1ll*b.x*a.y;}
bool operator<(Node a,Node b){return Cross(a,b)<0;}
struct Line{Node v;int a,b;};
bool operator<(Line a,Line b)
{
	if(a.v<b.v)return true;
	if(b.v<a.v)return false;
	if(a.a!=b.a)return a.a<b.a;
	return a.b<b.b;
}
int n;
vector<Line> E;
ll f[MAX][MAX][6];
int main()
{
	n=read();
	for(int i=1;i<=n;++i)p[i].x=read(),p[i].y=read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			if(i!=j)E.push_back((Line){(Node){p[j]-p[i]},i,j});
	sort(E.begin(),E.end());
	for(auto a:E)
	{
		int u=a.a,v=a.b;
		f[u][v][1]+=1;
		for(int i=1;i<=5;++i)
			for(int j=1;j<=n;++j)
				f[j][v][i+1]+=f[j][u][i];
	}
	ll ans=0;
	for(int i=1;i<=n;++i)ans+=f[i][i][5];
	printf("%lld\n",ans);
	return 0;
}
posted @ 2019-04-23 15:17  小蒟蒻yyb  阅读(621)  评论(3编辑  收藏  举报