• S2考前综合刷题营Day1


    打扑克

    题目描述

    皮蛋为了讨好黑妞,想要跟她打扑克。

    他们打的扑克是这样一种规则:有面值大小从 (1)(n) 的扑克各一张。其中奇数牌在皮蛋手中,偶数牌在黑妞手中。每人每次只能出一张牌,先出完者获胜(遵循最基本的扑克规则:当对手出牌后,可以选择出一张比他大的牌,或者不管,让他再任意出一张牌)。假设皮蛋和黑妞都是足够聪明的人,都想让自己获胜。现在给定 (n) 和谁先出牌,那么谁会获胜呢?

    输入格式

    (1)(1) 个正整数 (T),表示数据组数。

    接下来 (T) 行,每行 (2) 个正整数 (n,op),表示打一局牌。其中 (n) 如题所示,保证 (op∈{0,1})(op=0) 表示皮蛋先出牌,(op=1) 表示黑妞先出牌。

    输出格式

    (T) 行每行 (1) 个数表示打一局牌的答案。(0) 表示皮蛋获胜,(1) 表示黑妞获胜。

    样例数据

    input

    2
    5 0
    10 1
    

    output

    0
    1
    

    数据规模与约定

    对于 (40\%) 的数据,(n≤10)

    对于 (70\%) 的数据,(n≤10000)

    对于 (100\%) 的数据,(2≤n≤101000,T≤100)

    时间限制:(1s)
    空间限制:(512MB)

    Solution

    分奇偶,分先后,简单模拟找规律即可。
    发现只有当牌数为奇数且皮蛋先手时,皮蛋才能赢,否则都是黑妞赢。
    要注意特判 (n=2) 的情况。

    粉刷匠

    题目描述

    皮蛋喜欢黑妞,想为她粉刷一面网格墙。

    墙上有 (n)(m) 列共 (n∗m) 个网格。初始时,整面墙都是红色的。皮蛋只有红、蓝两种颜色的油漆,而且皮蛋很懒,他每次刷墙只会把某一整行或某一整列刷成红色或蓝色。皮蛋一共粉刷了 (k) 次墙。现在给出皮蛋的粉刷方案,请你求出最后这面墙有多少个格子是蓝色的。

    输入格式

    (1)(3) 个正整数 (n,m,k)

    接下来 (k) 行,每行 3$ 个整数 (x,y,z) 表示一次刷墙。保证 (x,z∈{0,1}) x,(x=0) 表示皮蛋粉刷第 (y) 行,否则表示粉刷第 (y) 列;(z=0) 则表示将该行(列)的格子都粉刷成红色,否则表示都粉刷成蓝色。保证操作合法。

    输出格式

    (1)(1) 个数表示答案。

    样例数据

    input

    2 2 2
    0 1 1
    1 2 1
    

    output

    3
    

    数据规模与约定

    对于 (30\%) 的数据,(1≤n,m,k≤1000)

    对于另外 (30\%) 的数据,(n≤10,1≤m,k≤100000)

    对于 (100\%) 的数据,(1≤n,m,k≤1000000)

    时间限制:(2s)
    空间限制:(512MB)

    Solution

    考虑到对于每一个格子,只有最后一次的行或列的修改对它有效,所以我们可以倒着考虑
    这样,每个格子就只被修改了一次,那么对于被修改的某一行或某一列,以后不会再考虑了,所以我们可以把它删掉。
    对于行的修改,如果是将这一行改为蓝色,那么我们将 (ans+=m(m)为列数()),同时 (n--(n)为行数);修改列同理。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define ll long long
    using namespace std;
    int read()
    {
    	char ch=getchar();
    	int a=0,x=1;
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') x=-x;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		a=(a<<1)+(a<<3)+(ch^48);
    		ch=getchar();
    	}
    	return a*x;
    }
    const int N=1e6+5;
    int n,m,k;
    int opt[N],x[N],y[N];
    bool vis[2][N];
    ll ans;
    int main()
    {
    	n=read();m=read();k=read();
    	for(int i=1;i<=k;i++)
    	{
    		opt[i]=read();
    		x[i]=read();
    		y[i]=read();
    	}
    	for(int i=k;i>=1;i--)
    	{
    		if(vis[opt[i]][x[i]]) continue;
    		vis[opt[i]][x[i]]=1;
    		if(opt[i]==0) ans+=m*y[i],n--;
    		else ans+=n*y[i],m--;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    直线竞速

    题目描述

    一年一度的繁荣山庄直线竞速比赛要开始了!

    本次比赛共有 (n) 位选手,第 (i) 位选手的起点在 (a_i) 位置,速度是 (v_i),速度方向是正方向。

    之后有 (Q) 个询问,每次询问经过 (t) 秒后,排名在第 (k) 位的选手的编号(在相同位置时,编号小的排名靠前)。

    输入格式

    (1)(1) 个正整数 (n)

    接下来 (n) 行,每行 (2) 个正整数 (v_i,a_i)

    接下来 (1)(1) 个整数 (Q)

    接下来 (Q) 行,每行 (2) 个正整数 (t,k),表示一个询问。

    输出格式

    (Q) 行,每行一个整数表示询问的答案。

    样例数据

    input

    4
    2 100
    3 50
    4 60
    5 1
    4    
    1 1
    50 2
    60 4
    100 1
    

    output

    1
    4
    1
    4
    

    数据规模与约定

    对于 (30\%) 的数据,(1≤n,Q≤1000)

    对于另外 (40\%) 的数据,(k=1)

    对于 (100\%) 的数据,(1≤n,Q≤7000)(1≤t≤10^9)(1≤v_i,a)i≤10^5$,(1≤k≤n)

    时间限制:(2s)
    空间限制:(512MB)

    Solution

    40pts

    对于每次询问,(O(n)) 求出此刻每个人的位置,然后从大到小排序,输出第 (k) 大的人编号。
    时间复杂度 (O(Qnlog{n}))

    70pts

    (40pts) 的基础上,对于 (k=1) 的部分,我们求出每个人位置的同时维护最大位置和这个人的编号,然后输出即可。
    时间复杂度 (O(Qn))

    100pts

    我的做法:考虑到当时间 (t) 逐渐增大时,决定人和人之间的位置关系的主要是他们的速度,所以我们可以求一个时间 (T),表示在 (T) 时刻之后他们的相对位置不再变化,可以求得
    (T) 的最大范围是 (10^5),所以如果 (t>=T),我们直接输出速度排名第 (k) 的人的编号;否则 (O(Onlog{n})) 暴力求得第 (k) 小的人的编号。
    时间复杂度 (O(Qnlog{n}-)玄学())

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define ll long long
    using namespace std;
    int read()
    {
    	char ch=getchar();
    	int a=0,x=1;
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') x=-x;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		a=(a<<1)+(a<<3)+(ch^48);
    		ch=getchar();
    	}
    	return a*x;
    }
    const int N=7005;
    struct node
    {
    	int x,v,Id;
    	ll now;
    }a[N],b[N];
    int n,q;
    double dp[N][N],T;
    bool cmp(node x,node y)
    {
    	if(x.v==y.v) return x.x>y.x;
    	return x.v>y.v;
    }
    bool Cmp(node x,node y)
    {
    	if(x.now==y.now) return x.Id<y.Id;
    	return x.now>y.now;
    }
    double max(double a,double b)
    {
    	if(a>b) return a;
    	return b;
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    	{
    		a[i].v=read();
    		a[i].x=read();
    		a[i].Id=i;
    	}
    	sort(a+1,a+1+n,cmp);
    	memset(dp,0x3fff,sizeof(dp));
    	for(int i=1;i<n;i++)
    	{
    		for(int j=i+1;j<=n;j++)
    		{
    			if(a[i].x>=a[j].x) dp[i][j]=0;
    			else 
    			{
    				dp[i][j]=1.0*(a[j].x-a[i].x)/(a[i].v-a[j].v);
    				T=max(T,dp[i][j]);
    			}
    		}
    	}
    	q=read();
    	for(int i=1;i<=q;i++)
    	{
    		int t=read();
    		int k=read();
    		if(1.0*t>=T) printf("%d
    ",a[k].Id);
    		else
    		{
    			if(k==1)
    			{
    				ll ans=-1e15,ansid;
    				for(int i=1;i<=n;i++)
    				{
    					a[i].now=a[i].x+t*a[i].v;
    					if(a[i].now>ans)
    					{
    						ans=a[i].now;
    						ansid=a[i].Id;
    					}
    				}
    				printf("%d
    ",ansid);
    			}
    			else
    			{
    				for(int i=1;i<=n;i++)
    				{
    					b[i].now=a[i].x+t*a[i].v;
    					b[i].Id=a[i].Id;
    				}
    				sort(b+1,b+1+n,Cmp);
    				printf("%d
    ",b[k].Id);
    			}
    		}
    	}
    	return 0;
    }
    

    正解做法:
    显然,任意两位选手的相对位置最多只会变化一次,所有选手在整个比赛期间的两两相对位置的交换次数最多是 (frac{n(n-1)}{2})
    于是我们把询问按时间排序,维护比赛期间选手的排名,对于每次询问,像冒泡排序一样从前往后把每位选手向前冒泡,总的交换次数是 (O(n^2)) 的。
    时间复杂度 (O(n^2+nQ+Qlog{Q}))

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<cmath>
    #include<set>
    #include<map>
    #include<cstdlib>
    #include<bitset>
    #include<stack>
    #include<ctime>
    #include<fstream>
    #define dd double
    #define ll long long
    #define mp make_pair
    #define pb push_back
    #define N 7010
    #define M 1010
    using namespace std;
    int n,Q;
    struct ma
    {
    	int v,a,num;
    }w[N];
    bool cmp1(ma x,ma y)
    {
    	return x.a>y.a;
    }
    struct am
    {
    	int t,k,ans,num;
    }q[N];
    bool cmp2(am x,am y)
    {
    	return x.t<y.t;
    }
    bool cmp3(am x,am y)
    {
    	return x.num<y.num;
    }
    int main()
    {
    	cin>>n;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d%d",&w[i].v,&w[i].a);
    		w[i].num=i;
    	}
    	cin>>Q;
    	for(int i=1;i<=Q;i++)
    	{
    		scanf("%d%d",&q[i].t,&q[i].k);
    		q[i].num=i;
    	}
    	sort(w+1,w+n+1,cmp1);
    	sort(q+1,q+Q+1,cmp2);
    	for(int i=0;i<=Q;i++)
    	{
    		for(int j=1;j<=n;j++)
    		{
    			for(int k=j;k>1;k--)
    			{
    				ll x=w[k].a+(ll)w[k].v*q[i].t;
    				ll y=w[k-1].a+(ll)w[k-1].v*q[i].t;
    				if(y<x||y==x&&w[k-1].num>w[k].num) swap(w[k-1],w[k]);
    				else break;
    			}
    		}
    		q[i].ans=w[q[i].k].num;
    	}
    	sort(q+1,q+Q+1,cmp3);
    	for(int i=1;i<=Q;i++) printf("%d
    ",q[i].ans);
    }
    

    游戏

    题目描述

    有一个游戏:给定两个正整数序列 (A,B),长度分别为 (n,m)。你可以做如下操作:删掉 (A) 的最后 (x(x≥1)) 个数并得到它们的和 (S_1),同时删掉 (B) 的最后 (y(y≥1)) 个数并得到它们的和 (S_2),这次操作的花费是 ((S_1–x)(S_2–y))。你需要一直操作直到 (A,B) 都为空(不能做完一次操作后使得其中一个为空而另一个非空)。本次游戏的总花费是每次花费的和。求最小的总花费。

    输入格式

    (1)(2) 个正整数 (n,m)

    (2)(n) 个正整数,表示序列 (A)

    (3)(m) 个正整数,表示序列 (B)

    输出格式

    (1)(1) 个数表示最小的总花费。

    样例数据

    input

    3 2
    1 2 3
    1 2
    

    output

    2
    

    数据规模与约定

    数据点 (1:n=20,m=20)

    数据点 (2:n=110,m=80)

    数据点 (3:n=200,m=130)

    数据点 (4:n=400,m=80)

    数据点 (5:n=1000,m=333)

    数据点 (6:n=510,m=910)

    数据点 (7:n=1200,m=1400)

    数据点 (8:n=700,m=1800)

    数据点 (9:n=1998,m=1370)

    数据点 (10:n=2000,m=1999)

    对于 (100\%) 的数据,(1≤A_i,B_i≤1000)

    时间限制:(2s)
    空间限制:(512MB)

    Solution

    首先化简一下题目,把每个数 (--),问题就变成了区间和直接乘。
    (f[i][j]) 表示 (A) 的前 (i) 个数和 (B) 的前 (j) 个数做游戏的最小总花费。
    (F[i][j]=min(f[k][r]+(S_A[i]-S_A[k])*(S_B[j]-S_B[r])),0<=k<i,0<=r<j) (S_A,S_B) 表示 (A,B) 的前缀和。
    时间复杂度 (O(n^2m^2))
    一个结论:存在一个最优解的每次删数,至少有一段长度是 (1)
    于是状态转移只需要枚举一维 时间复杂度 (O(nm(n+m)))
    有了之前的结论,可以重新思考转移的状态:
    (1.)(A)(B) 截取的最后一段长度均为 (1),则有转移为:(f[i][j]=min(f[i][j],f[i-1][j-1]+a[i]*b[j]))
    (2.)(A) 的最后一段长度为 (1)(B) 的长度不为 (1),则有转移 (f[i][j]=min(f[i][j],f[i-1][k]+a[i]*(b[k+1]+b[k+2]+...+b[j])))
    对照上一种情况的转移方程,换种角度来思考:我们用长度为 (1)(A) 序列和长度为 (k)(B) 序列对应,其实就是第一种情况执行了 (k) 次,因为无论如何,(a[i]*b[j]) 的贡献都会加进去的。
    则有转移方程:(f[i][j]=min(f[i][j],f[i][j-1]+a[i]*b[j]))(就是让 (B) 序列的后几个依次与 (a[i]) 对应)。
    (3.)(B) 的最后一段长度为 (1)(A) 的长度不为 (1),此情况与情况 (2) 同理,转移方程为:(f[i][j]=min(f[i][j],f[i-1][j]+a[i]*b[i]))
    综上,给出最后的转移方程:(f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+a[i]*b[j])
    边界条件:(f[0][0]=0)
    时间复杂度:(O(nm))

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define ll long long
    using namespace std;
    int read()
    {
    	char ch=getchar();
    	int a=0,x=1;
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') x=-x;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		a=(a<<1)+(a<<3)+(ch^48);
    		ch=getchar();
    	}
    	return a*x;
    }
    const int N=2005;
    int n,m;
    int a[N],b[N];
    ll f[N][N];
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;i++) a[i]=read()-1;
    	for(int i=1;i<=m;i++) b[i]=read()-1;
    	memset(f,127,sizeof(f));
    	f[0][0]=0;
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=m;j++)
    		{
    			f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+a[i]*b[j];
    		}
    	}
    	printf("%lld
    ",f[n][m]);
    	return 0;
    }
    
  • 相关阅读:
    Sql日期时间格式转换
    基于现有数据库的Code First模式迁移更新数据库
    EF Code First Migrations数据库迁移
    IE6 IE7 ‘JSON’ 未定义
    解决iis7只能上传30M文件的限制
    在ASP.NET MVC中使用Juqery实现页面局部刷新
    asp.net mvc3 利用Ajax实现局部刷新
    CS0234: 命名空间“System.Web.Mvc”中不存在类型或命名空间名称“Html、Ajax”(是否缺少程序集引用?)
    陨石坑之webapi使用filter
    Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码
  • 原文地址:https://www.cnblogs.com/xcg123/p/13760172.html
Copyright © 2020-2023  润新知