zoukankan      html  css  js  c++  java
  • 【CF1146】Forethought Future Cup

    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);
    	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)次。
    (nle 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
    ",Query(A,B));fflush(stdout);
    	}
    	return 0;
    }
    

    D. Frog Jumping

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

    先来看看(f(n))怎么求,大力猜想一下一定能够从(f(n-a))转移过来,然后再考虑一下中间这(a)步能够走到些什么地方,大力猜猜可以走到所有((n-a)+gcd(a,b))倍数的位置,所以大力猜想一下(f(n+a)=f(n)+frac{a}{gcd(a,b)})
    然而这样子在(n)很小的时候肯定是假的,估摸一下(nge a+b)的时候是真的,其他时候是假的。
    但是(a+ble 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
    ",ans);
    	return 0;
    }
    

    E. Hot is Cold

    一开始有一个数列,你要执行(n)次操作,每次给你一个(>x)或者(<x),如果一个数带进去合法,就把他变成相反数,求出最后的数列。
    (n,Qle 2*10^5,-10^5le a_ile 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)中的叶子节点都被连通。
    求划分的方案数。
    (nle 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
    ",(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,mle 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
    ",ans-Dinic());
    	return 0;
    }
    

    H. Satanic Panic

    平面上有一堆点,你要求五角星的数量,保证不存在三点共线。
    (nle 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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    P2184 贪婪大陆
    codeforces-1348-D Phoenix and Science
    联系我
    留言板
    友链
    java集合ArrayList按指定字段排序
    linux下设置oracle开机自启动
    微信公众号开发参考教程
    java生成快递单并调用打印机打印
    java生成128A条形码
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10756600.html
Copyright © 2011-2022 走看看