20210717 noip18

考前

从小饭桌出来正好遇到雨下到最大,有伞但还是湿透了
路上看到一个猛男搏击暴风雨
到了机房收拾了半天才开始考试
ys 他们小饭桌十分明智地在小饭桌看题,雨下小了才来

考场

状态很差。

开题,一点想法都没有
T1 尝试推出 \(\frac AB\) 的范围,结果因为忘记不等式同乘负数要变号卡了好久。看最终的式子有点像凸包,但不知道怎么用
一看 T2 人都傻了,现场学习盖斯定律,仔细想想 whk 怎么算这个东西,想起来一次周练 5 道题错了 4 个。靠 \(n,m\le200\) 和样例解释猜出是高斯消元,回头再看吧
T3 莫名熟悉,但并不影响我只会暴力。甚至没想到二分答案

不记得什么时候开始写的了,只记得 T1 调了好久,打了一堆补丁,要是没有大样例绝对爆 \(0\) 了。T3 的暴力写的比较顺,\(k=1\) 的分也好拿,但 \(k=n\) 的没什么思路。
16.50 开始看 T2,手模高斯消元确认无误后开始写,但忘了很多,一直到 17.30 才写完,过了所有样例。
继续想 T3 \(k=n\) 的做法,还是不会,打了随机化贪心,竟然过了样例?!

res

rk3 30+85+30
T1 还是少个特判
T2 高斯消元板子挂了
T3 暴力挂了一个点,但随机化贪心过了一个。。。

感觉这场策略有点问题,应该先写 T2 正解(毕竟不难写)。T1 浪费了不少时间,反而 T2 没写多久,如果考场上没写出高斯消元就爆 \(0\) 了,连骗分都没时间写。

rk1 yzf 100+80+0
rk4 王天泽 20+95+5
rk12 wcr 10+0+70

导弹袭击

时间 \(t=\frac Aa+\frac Bb\),把 \((\frac 1a,\frac 1b)\) 看做点,变形得到 \(y=-\frac ABx+\frac tB\),要求一个 \(A,B\) 使 \(t\) 最小,即存在 \(\frac AB\) 使这个一次函数在 \((\frac 1a,\frac 1b)\) 处截距最小,这样的点构成了一个左下凸包(斜率为负),单调栈维护即可。

注意 \(a,b\) 很大而分子为 \(1\),因此精度掉的很多,那么只在算斜率时转成 long double,其他时候用 int
注意去重和先排掉一定不优的答案(\(a,b\) 都比另一个小 \(a,b\)

int
const int N = 3e5+5;
int n;
struct Node {
	int a,b,id;
} p[N];

const LD eps = 1e-15;
int tp,stk[N];
bool vis[N],ans[N];

bool equ(LD x,LD y) { return fabs(x-y) < eps; }
LD K(int i,int j)
	{ return (LD)p[i].a*p[j].a*(p[i].b-p[j].b)/p[i].b/p[j].b/(p[i].a-p[j].a); }

signed main() {
	read(n);
	For(i,1,n) read(p[i].a,p[i].b), p[i].id = i;
	sort(p+1,p+n+1,
		[](const Node &x,const Node &y){return x.a!=y.a?x.a>y.a:x.b>y.b;});
	int mxb = 0;
	For(i,1,n)
		if( p[i].b <= mxb ) vis[i] = 1;
		else mxb = p[i].b;
	stk[++tp] = 1;
	For(i,2,n) {
		if( vis[i] || (tp && K(stk[tp],i) > 0) ) continue;
		while( tp > 1 && K(stk[tp],i)+eps < K(stk[tp],stk[tp-1]) ) --tp;
		stk[++tp] = i;
	}
	for(int t; t = stk[tp], tp; --tp) {
		ans[p[t].id] = 1;
		for(int i = t+1; i <= n && p[i].a==p[t].a&&p[i].b==p[t].b; ++i)
			ans[p[i].id] = 1;
	}
	For(i,1,n) if( ans[i] ) write(i,' ');
	return ioclear();
}
double(yzf 的考场代码)
#include<iostream>
#include<cstdio>
#include<algorithm> 
#define ll long long
using namespace std;
int read()
{
    int ret=0;char c=getchar();
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();
    return ret;
}
const int maxn=3e5+5;
const double eps=1e-30;
int n;
struct point
{
    double x,y;int num;
    const bool operator <(const point &a)const{if(x!=a.x)return x>a.x;return y>a.y;}
    const bool operator ==(const point &a)const{return x==a.x&&y==a.y;}
    friend bool judge(point a,point b,point c){return c.x*a.y*(a.x-b.x)*(b.y-c.y)<c.y*a.x*(a.y-b.y)*(b.x-c.x);}
}p[maxn];
bool used[maxn];int sta[maxn],top;
int ans[maxn],cnt;
int main()
{
    n=read();
    for(int i=1;i<=n;i++)p[i].x=read(),p[i].y=read(),p[i].num=i;
    sort(p+1,p+n+1);
    for(int i=1;i<=n;i++)
    {
        if(top&&p[sta[top]]==p[i])continue;
        if(top&&p[i].y<=p[sta[top]].y)continue;
        while(top>=2&&judge(p[sta[top-1]],p[sta[top]],p[i]))used[sta[top--]]=0;
        used[sta[++top]=i]=1;
    }
    for(int i=2;i<=n;i++)
        if(p[i]==p[i-1]&&used[i-1])used[i]=1;
    for(int i=1;i<=n;i++)if(used[i])ans[++cnt]=p[i].num;
    sort(ans+1,ans+cnt+1);
    for(int i=1;i<=cnt;i++)printf("%d ",ans[i]);
    return 0;
}

炼金术士的疑惑

官方题解有严格证明。

感性理解:
把每个物质的内能看做未知量,显然 \(\Delta H\) 与方程式可以一起消,那么消元时对要求解的方程一起操作,等要求解的方程各系数都消成 \(0\) 时取常数项的相反数就是答案

code
const int N = 205;
int n;

const double eps = 1e-8;
int m;
double a[N][N];
map<string,int> mp;

void debug() {
	For(i,1,n+1) {
		For(j,1,m+1) cout<<a[i][j]<<' ';
		cout<<endl;
	}
	cout<<endl;
}

bool equ(double x,double y) { return fabs(x-y) < eps; }
bool check() {
	For(i,1,m) if( !equ(a[n+1][i],0) ) return 0;
	return 1;
}

signed main() {
	ios::sync_with_stdio(0);cin.tie(0);
	cin>>n;
	For(i,1,n+1) {
		for(int f = 1;;) {
			double x; string y; cin>>x>>y;
			if( !mp.count(y) ) mp[y] = ++m;
			a[i][mp[y]] = f*x;
			cin>>y;
			if( y == "=" ) f = -1;
			else if( y == "H=" ) break;
		}
		if( i != n+1 ) cin>>a[i][201];
	}
	m = 200;
	For(i,1,m) {
		int p = i;
		For(j,i+1,n) if( fabs(a[j][i]) > fabs(a[p][i]) ) p = j;
		if( p != i ) swap(a[i],a[p]);
		rFor(j,m+1,i) a[i][j] /= a[i][i];
		For(j,1,n+1) if( j != i )
			rFor(k,m+1,i) a[j][k] -= a[j][i] * a[i][k];
		if( check() ) break;
	}
	printf("%.1Lf",equ(a[n+1][m+1],0) ? 0 : -a[n+1][m+1]);
	return 0;
}

老司机的狂欢

考虑二分答案。

将司机按初始位置排序,check 时先求出结束位置,在对结束位置求 LIS(LIS 中的司机不会相遇),若 LIS 长度小于 \(k\) 则缩小答案。时间复杂度 \(O(n\log n\log86400)\)

确定答案后再求 LIS,若长度大于 \(k\) 则为 \(-1\),否则考虑构造字典序最小的方案。
转移时对于 LIS 长度相同的位置,如果从前往后一个一个比显然会 TLE,但我们可以用倍增加速这个过程。(如果把转移看成一颗树,那就相当于在倍增求 LCA 的过程中算两种方案不同的部分中各自最小的位置)

写的时候注意边界、编号

code
const int N = 1e5+5;

template<class T>
class BIT {
private:
	int n,ind,tag[N];
	T zero,t[N];
	int lowbit(int x) { return x&-x; }
public:
	void init(int x,T y) { n = x, zero = y; ++ind; }
	void add(int i,T x) {
		for(; i <= n; i += lowbit(i)) {
			if( tag[i] != ind ) t[i] = zero, tag[i] = ind;
			t[i] = max(t[i],x);
		}
	}
	T query(int i) {
		T res = zero;
		for(; i; i -= lowbit(i)) {
			if( tag[i] != ind ) t[i] = zero, tag[i] = ind;
			res = max(res,t[i]);
		}
		return res;
	}
};

int n,k;
struct Driver {
	LL x,a;
	int id;
} d[N];

int fa[N][17],mn[N][17];
LL p[N],mp[N];
bool ans[N];
BIT<int> bit1;
struct Node {
	int dep,pos;
	Node (int dep=0,int pos=0):dep(dep),pos(pos){}
};
bool operator < (const Node &x,const Node &y) {
	if( x.dep != y.dep ) return x.dep < y.dep;
	int u = x.pos, v = y.pos, mnu = u, mnv = v;
	rFor(i,16,0) if( fa[u][i] != fa[v][i] ) {
		mnu = min(mnu,mn[u][i]), u = fa[u][i];
		mnv = min(mnv,mn[v][i]), v = fa[v][i];
	}
	return mnu > mnv;
}
BIT<Node> bit2;

void lsh(int t) {
	For(i,1,n) p[i] = mp[i] = 2*d[i].x+d[i].a*t*t;
	sort(mp+1,mp+n+1), mp[0] = unique(mp+1,mp+n+1)-mp-1;
	For(i,1,n) p[i] = lower_bound(mp+1,mp+mp[0]+1,p[i])-mp;
}
int check(int t) {
	lsh(t);
	bit1.init(n,0);
	For(i,1,n) bit1.add(p[i],bit1.query(p[i]-1)+1);
	return bit1.query(n);
}

signed main() {
	read(n,k);
	For(i,1,n) read(d[i].x,d[i].a), d[i].id = i;
	sort(d+1,d+n+1,[](const Driver &x,const Driver &y){return x.x<y.x;});
	int l = 0, r = 86400;
	while( l < r ) {
		int mid = l+r+1>>1;
		if( check(mid) < k ) r = mid-1;
		else l = mid;
	}
	write(l);
	if( check(l) > k ) { write(-1); return iocl(); }
	memset(mn,0x3f,sizeof mn);
	lsh(l);
	bit2.init(n,Node(0,0));
	For(i,1,n) {
		Node pre = bit2.query(p[i]-1);
		int u = d[i].id;
		fa[u][0] = mn[u][0] = pre.pos;
		For(j,1,16) fa[u][j] = fa[fa[u][j-1]][j-1],
					mn[u][j] = min(mn[u][j-1],mn[fa[u][j-1]][j-1]);
		bit2.add(p[i],Node(pre.dep+1,u));
	}
	for(int i = bit2.query(n).pos; i; i = fa[i][0]) ans[i] = 1;
	For(i,1,n) if( ans[i] ) write(i);
	return iocl();
}
posted @ 2021-07-18 09:30  401rk8  阅读(78)  评论(0编辑  收藏  举报