7.17考试总结(NOIP模拟18)[导弹袭击·炼金术士的疑惑·老司机的狂欢]

问灵十三载,等一不归人。

前言

这回考试全靠 T2 了,别的基本上没分(菜)

总感觉最近进度有亿点快,每天都在补坑,每天都在留坑。。。。

T1 导弹袭击

解题思路

因为这个题的两种长度是不一定的,因此,显然什么二分枚举都不对了。

通过细致阅读题目,我们可以得到一种 60pts 的做法:

通过 \(n^2\) 枚举任意两种导弹的较另一种时间的临界值,近而求出一个范围。

判断两个范围是否有交集就好了,大概就是下面这个柿子(假设想让 1 较于 2 更优)

  1. \(\dfrac{L_A}{L_B}\le \dfrac{a_1\times a_2 \times (b_1-b_2)}{b_1 \times b_2\times (a_2-a_1)}(a_1>a_2)\)

  2. \(\dfrac{L_A}{L_B}\ge \dfrac{a_1\times a_2 \times (b_1-b_2)}{b_1 \times b_2\times (a_2-a_1)}(a_1<a_2)\)

并且,我们还可以缩小一下值域,对于在相同的 a,b 不是最大的,或者相同的 b,a 不是最大的,都可以直接干掉。

正解的思路大同小异,把 \((\dfrac{1}{a_i},\dfrac{1}{b_i})\) 当作坐标系里面的一个点,用凸包维护斜率。

code

60pts

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=3e5+10,INF=1e9;
int n,cnt,a[N],b[N];
map<int,int>maxa,maxb;
bool vis[N];
struct Node
{
	double a,b;
	int id;
}s[N];
bool comp(Node x,Node y)
{
	return x.a<y.a;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		b[i]=read();
		maxa[a[i]]=max(maxa[a[i]],b[i]);
		maxb[b[i]]=max(maxb[b[i]],a[i]);
	}
	for(int i=1;i<=n;i++)
	{
		if(maxa[a[i]]==b[i]&&maxb[b[i]]==a[i])
			s[++cnt]=(Node){a[i],b[i],i};
	}
	sort(s+1,s+cnt+1,comp);
	for(int i=1;i<=cnt;i++)
	{
		bool jud=false;
		double l=0,r=INF;
		for(int j=1;j<i;j++)
			l=max(l,s[i].a*s[j].a*(s[i].b-s[j].b)/(s[i].b*s[j].b*(s[j].a-s[i].a)));
		for(int j=i+1;j<=cnt;j++)
		{
			if(s[i].b<s[j].b)
			{
				jud=true;
				break;
			}
			r=min(r,s[i].a*s[j].a*(s[i].b-s[j].b)/(s[i].b*s[j].b*(s[j].a-s[i].a)));
		}
		if(l>r||l<0||r<0)	jud=true;
		vis[s[i].id]=jud^1;
	}
	for(int i=1;i<=n;i++)
		if(vis[i])
			cout<<i<<' ';
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=3e5+10,INF=1e9;
int n,cnt,a[N],b[N];
int top,sta[N];
double k[N];
vector<int> v[N];
bool vis[N];
struct Node
{
	double a,b;
	int id;
}s[N];
bool comp(Node x,Node y)
{
	if(x.a!=y.a)	return x.a>y.a;
	return x.b>y.b;
}
double K(int i,int j)
{
	return s[i].a*s[j].a*(s[j].b-s[i].b)/(s[i].b*s[j].b*(s[j].a-s[i].a));
}
signed main()
{
	int rx,ry=0;
	n=read();
	for(int i=1;i<=n;i++)
	{
		s[i].a=read();
		s[i].b=read();
		s[i].id=i;
		if(ry<s[i].b||(ry==s[i].b&&rx<s[i].a))
			ry=s[i].b,rx=s[i].a;
	}
	sort(s+1,s+n+1,comp);
	sta[++top]=1;
	v[1].push_back(s[1].id);
	for(int i=2;i<=n&&rx<=s[i].a;i++)
	{
		if(s[i].a==s[sta[top]].a)
		{
			if(s[i].b==s[sta[top]].b)
				v[sta[top]].push_back(s[i].id);
			continue;
		}
		v[i].push_back(s[i].id);
		while(top>1&&k[sta[top]]>K(sta[top],i))	top--;
		k[i]=K(sta[top],i);
		sta[++top]=i;
	}
	for(int i=1;i<=top;i++)
		for(int j=0;j<v[sta[i]].size();j++)
			vis[v[sta[i]][j]]=true;
	for(int i=1;i<=n;i++)
		if(vis[i])
			printf("%lld ",i);
	return 0;
}

T2 炼金术士的疑惑

解题思路

比较水的一个题,基本上都能一眼看出是高斯消元。

无非就是把方程右边的柿子移一下项。

对于已知方程直接消元再卡一下精度就可以得到 90pts 的巨额分数。

100pts 的做法其实就是把问题方程也放进去一起消,但是不要换到上面去。

最后问题柿子的结果取个相反数就是答案了。。(真没啥好说的)

code

90pts(可能消元方法有点诡异)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=210,M=1e3+10;
int n,cnt;
double num,answer,s[N][M],ans[N],q[N],val[N];
string ch,opt;
map<string,int> ma;
inline int read()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
//		cout<<i<<endl;
		scanf("%lf",&num);
		cin>>ch;
//		cout<<ch<<endl;
		if(!ma[ch])	ma[ch]=++cnt;
		s[i][ma[ch]]=num;
		do
		{
			cin>>opt;
//			cout<<opt<<endl;
			if(opt[0]=='=')	break;
			scanf("%lf",&num);
			cin>>ch;
//			cout<<ch<<endl;
			if(!ma[ch])	ma[ch]=++cnt;
			s[i][ma[ch]]=num;
		}while(opt[0]!='=');
//		cout<<i<<endl;
		scanf("%lf",&num);
//		cin>>num;
//		cout<<num<<endl;
		cin>>ch;
//		cout<<ch<<endl;
		if(!ma[ch])	ma[ch]=++cnt;
		s[i][ma[ch]]=-num;
		do
		{
			cin>>opt;
//			cout<<opt<<endl;
			if(opt[0]=='H')	break;
			scanf("%lf",&num);
			cin>>ch;
//			cout<<ch<<endl;
			if(!ma[ch])	ma[ch]=++cnt;
			s[i][ma[ch]]=-num;
		}while(opt[0]!='H');
		scanf("%lf",&ans[i]);
	}
	for(int i=1;i<=n;i++)
		s[i][cnt+1]=ans[i];
	scanf("%lf",&num);
	cin>>ch;
	if(!ma[ch])	ma[ch]=++cnt;
	q[ma[ch]]=num;
	do
	{
		cin>>opt;
		if(opt[0]=='=')	break;
		scanf("%lf",&num);
		cin>>ch;
		if(!ma[ch])	ma[ch]=++cnt;
		q[ma[ch]]=num;
	}while(opt[0]!='=');
	scanf("%lf",&num);
	cin>>ch;
	if(!ma[ch])	ma[ch]=++cnt;
	q[ma[ch]]=-num;
	do
	{
		cin>>opt;
		if(opt[0]=='H')	break;
		scanf("%lf",&num);
		cin>>ch;
		if(!ma[ch])	ma[ch]=++cnt;
		q[ma[ch]]=-num;
	}while(opt[0]!='H');
}
void check()
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=cnt+1;j++)
			cout<<s[i][j]<<' ';
		cout<<endl;
	}
	for(int j=1;j<=cnt;j++)
		cout<<q[j]<<' ';
}
void gaosi()
{
	for(int i=1;i<=n;i++)
	{
		int pos=0;
		for(int j=1;j<=cnt;j++)
			if(s[i][j])
			{
				pos=j;
				break;
			}
		if(s[i][pos]!=1&&s[i][pos])
		{
			double temp=s[i][pos];
			for(int j=pos;j<=cnt+1;j++)
				s[i][j]/=temp;
		}
		for(int j=i+1;j<=n;j++)
		{
			if(!s[j][pos])
				continue;
			double temp=s[j][pos];
			for(int k=pos;k<=cnt+1;k++)
				s[j][k]-=s[i][k]*temp;
		}
	}
	for(int i=n;i>=2;i--)
	{
		int pos=0;
		for(int j=1;j<=cnt;j++)
			if(s[i][j])
			{
				pos=j;
				break;
			}
		if(s[i][pos]!=1&&s[i][pos])
		{
			double temp=s[i][pos];
			for(int j=pos;j<=cnt+1;j++)
				s[i][j]/=temp;
		}
		for(int j=1;j<i;j++)
		{
			if(!s[j][pos])
				continue;
			double temp=s[j][pos];
			for(int k=pos;k<=cnt+1;k++)
				s[j][k]-=s[i][k]*temp;
		}
	}
}
void solve()
{
	gaosi();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=cnt;j++)
			if(s[i][j])
			{
				val[j]=s[i][cnt+1];
				break;
			}
	for(int i=1;i<=cnt;i++)
		answer+=q[i]*val[i];
	answer*=10;
	answer+=1e-6;
	printf("%.1lf",answer/10.0);
}
signed main()
{
	read();
	solve();
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=210,M=1e3+10;
int n,cnt;
double num,answer,s[N][M],ans[N],q[N],val[N];
string ch,opt;
map<string,int> ma;
inline int read()
{
	scanf("%lld",&n);
	n++;
	for(int i=1;i<=n;i++)
	{
		scanf("%lf",&num);
		cin>>ch;
		if(!ma[ch])	ma[ch]=++cnt;
		s[i][ma[ch]]=num;
		do
		{
			cin>>opt;
			if(opt[0]=='=')	break;
			scanf("%lf",&num);
			cin>>ch;
			if(!ma[ch])	ma[ch]=++cnt;
			s[i][ma[ch]]=num;
		}while(opt[0]!='=');
		scanf("%lf",&num);
		cin>>ch;
		if(!ma[ch])	ma[ch]=++cnt;
		s[i][ma[ch]]=-num;
		do
		{
			cin>>opt;
			if(opt[0]=='H')	break;
			scanf("%lf",&num);
			cin>>ch;
			if(!ma[ch])	ma[ch]=++cnt;
			s[i][ma[ch]]=-num;
		}while(opt[0]!='H');
		if(i!=n)	scanf("%lf",&ans[i]);
	}
	for(int i=1;i<n;i++)
		s[i][cnt+1]=ans[i];
}
void gaosi()
{
	for(int i=1,j=1;i<n&&j<=cnt;i++,j++)
	{
		int maxn=i;
		for(int k=i+1;k<=n-1;k++)
			if(fabs(s[k][j])>fabs(s[maxn][j]))
				maxn=k;
		if(maxn!=i)
			for(int k=1;k<=cnt+1;k++)
				swap(s[maxn][k],s[i][k]);
		if(!fabs(s[i][j]))
		{
			i--;
			continue;
		}
		for(int k=i+1;k<=n;k++)
			if(fabs(s[k][j]))
			{
				double temp=s[k][j]/s[i][j];
				for(int l=j;l<=cnt+1;l++)
					s[k][l]-=s[i][l]*temp;
			}
	}
}
void solve()
{
	gaosi();
	printf("%.1lf",-s[n][cnt+1]+1e-12);
}
signed main()
{
	read();
	solve();
	return 0;
}

T3 老司机的狂欢

解题思路

这可真是个阴间题,第一问还比较人性,第二问就变态。

大体思路都是对于序列进行树状数组进行优化。

对于第一问,一看 86400 ,就是二分答案就好了。

对于 \(x(i)<x(j)\) 的情况,需要满足 \(x(i)+\dfrac {a(i)\times t^2}{2} < x(j)+\dfrac {a(j)\times t^2}{2}\)

这好像就是普普通通的物理公式了吧。

那么把x离散作为数组下标,t时间后的位置作为值,合法的最多人数为最长上升子序列。

将t时间后的位置再次离散,并且用树状数组维护。

第二问与第一问差不多,其实就是在模拟第一问的过程,然后记录下最优的 id 。

考虑dp的转移;一个点只能有他之前的一个点转移过来,所以是一个树形结构。

设f[j]=f[k]且都可以转移到i,那么考虑转移的树形结构,j,k处于同一深度,且lca及以上的序列相同。

当j->lca这条路径上的最小值小于k->lca这条路径的最小值时j比k更优。

那么只要倍增维护前驱及最小值即可,同样也是用树状数组进行维护。

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10;
int n,m,tim=1,now,tre[N],a[N],lsh[N];
int f[N][25],minn[N][25],ans[N];
pair<int,int> tr[N];
struct Node
{
	int x,a,id;
}s[N];
bool comp(Node x,Node y)
{
	return x.x<y.x;
}
int lowbit(int x)
{
	return x&(-x);
}
void eadd(int x,int num)
{
	for(int i=x;i<=n;i+=lowbit(i))
		tre[i]=max(tre[i],num);
}
int eask(int x)
{
	int maxn=0;
	for(int i=x;i>0;i-=lowbit(i))
		maxn=max(maxn,tre[i]);
	return maxn;
}
bool check(int x)
{
	memset(tre,0,sizeof(tre));
	for(int i=1;i<=n;i++)
		lsh[i]=a[i]=s[i].a*x*x+2*s[i].x;
	sort(lsh+1,lsh+n+1);
	for(int i=1;i<=n;i++)
	{
		a[i]=lower_bound(lsh+1,lsh+n+1,a[i])-lsh;
		eadd(a[i],eask(a[i]-1)+1);
	}
	now=eask(n);
	return now>=m;
}
void Get_tim()
{
	sort(s+1,s+n+1,comp);
	int l=1,r=86400;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		{
			l=mid+1;
			tim=mid;
		}
		else 	r=mid-1;
	}
	check(tim);	
	printf("%lld\n",tim);
	if(now>m)
	{
		printf("-1");
		exit(0);
	}
}
bool judge(pair<int,int> x,pair<int,int> y)
{
	if(x.first!=y.first)
		return x.first<y.first;
	int minx=x.second,miny=y.second,x2=x.second,y2=y.second;
	for(int i=20;i>=0;i--)
		if(f[x2][i]!=f[y2][i])
		{
			minx=min(minx,minn[x2][i]);
			miny=min(miny,minn[y2][i]);
			x2=f[x2][i];
			y2=f[y2][i];
		}
	return minx>miny;
}
void add(int x,pair<int,int> val)
{
	for(int i=x;i<=n;i+=lowbit(i))
		if(judge(tr[i],val))
			tr[i]=val;
}
pair<int,int> ask(int x)
{
    pair<int,int> answer=make_pair(0,0);
    for(int i=x;i;i-=lowbit(i))
	    if(judge(answer,tr[i]))
			answer=tr[i];
    return answer;
}
void build(int x,int fat)
{
	f[x][0]=minn[x][0]=fat;
	for(int i=1;i<=20;i++)
    {
        f[x][i]=f[f[x][i-1]][i-1];
        minn[x][i]=min(minn[x][i-1],minn[f[x][i-1]][i-1]);
    }
}
signed main()
{
	memset(minn,0x3f,sizeof(minn));
	n=read();
	m=read();
	for(int i=1;i<=n;i++)
	{
		s[i].x=read();
		s[i].a=read();
		s[i].id=i;
	}
	Get_tim();
	for(int i=1;i<=n;i++)
		lsh[i]=a[i]=2*s[i].x+s[i].a*tim*tim;
	sort(lsh+1,lsh+n+1);
	for(int i=1;i<=n;i++)
	{
		a[i]=lower_bound(lsh+1,lsh+n+1,a[i])-lsh;
		pair<int,int> temp=ask(a[i]-1);
		build(s[i].id,temp.second);
		add(a[i],make_pair(temp.first+1,s[i].id));
	}
	now=ask(n).second;
	for(int i=1;i<=m;i++)
	{
		ans[i]=now;
		now=f[now][0];
	}
	sort(ans+1,ans+m+1);
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2021-07-19 14:26  Varuxn  阅读(59)  评论(0编辑  收藏  举报