noip模拟18[导弹袭击·炼金术士的疑惑·老司机的狂欢]
noip模拟18 solutions
分数还是可以,但是只是暴力分,真正的正解我理解了好久
。。。下一场都考完了我才改完
T1 导弹袭击
呵呵考试的时候直接维护的范围,比\(O(n^2)\)小那么一点点,但是根本过不了
我确实想到了是斜率优化,没有画图,然后不知道咋弄了就放弃了
考完改题的时候,我一直想着昨天的那个二分弹栈,然后手动给我的单调栈加了个log
hhh,然后看完std才改过来
我们让\(\frac{1}{a},\frac{1}{b}\)为他们的坐标,然后维护一个左下凸包就好了
推完式子发现,我们完全不需要AB,而且只有斜率为负的时候才有效,然后就维护就好了
最最最最重要的,要去重,然后绝对不可能的就不要往栈中放了,直接走人就好了
还有一个,千万不要用\(\frac{1}{a},\frac{1}{b}\)当作他们的坐标来计算,精度太小,根本卡不过
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ld long double
const int N=3e5+5;
int n;
struct POT{
ld x,y;
vector<int> id;
bool operator < (POT a)const{
if(a.x!=x)return a.x<x;
return a.y<y;
}
}sca[N];
int sta[N],top;
ld k[N],mx,my;
bool vis[N];
ld K(POT a,POT b){
return b.x*a.x*(b.y-a.y)/b.y/a.y/(b.x-a.x);
}
signed main(){
scanf("%d",&n);
for(re i=1;i<=n;i++){
scanf("%Lf%Lf",&sca[i].x,&sca[i].y);
sca[i].id.push_back(i);
if(my<sca[i].y)mx=sca[i].x,my=sca[i].y;
if(my==sca[i].y&&mx<sca[i].x)mx=sca[i].x,my=sca[i].y;
}
sort(sca+1,sca+n+1);
sta[top=1]=1;
for(re i=2;i<=n;i++){
if(mx>sca[i].x)break;
if(sca[i].x==sca[sta[top]].x){
if(sca[i].y==sca[sta[top]].y)
sca[sta[top]].id.push_back(sca[i].id[0]);
continue;
}
while(top>1&&k[top]>K(sca[sta[top]],sca[i]))top--;
sta[++top]=i;k[top]=K(sca[sta[top-1]],sca[i]);
}
for(re i=1;i<=top;i++)
for(re j=0;j<sca[sta[i]].id.size();j++)
vis[sca[sta[i]].id[j]]=true;
for(re i=1;i<=n;i++)if(vis[i])printf("%d ",i);
}
·
T2 炼金术士的疑惑
这个就是一个变相的高斯消元
然后,我们很容易就搞到一个正常的高斯消元,把方程式竖起来
但是随后我们就发现有可能有多组解,然后导致我们求不出来,我在考场上就止步于此了
所以我们就不需要求每一个解了
我们不需要把方程式竖起来,就直接向下消,题目保证有解,所以最后要求的方程式的所有系数都会消成0
那么此时的n+1位就是答案的负数
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=250;
int n;
map<string,int> mp;
double jz[N][N],hb[N];
int cnt;
double x[N],ans;
signed main(){
//string a;cin>>a;cout<<a;
scanf("%d",&n);
for(re i=1;i<=n+1;i++){
int typ=0;
while(1){
double x;
string c;
cin>>x>>c;
//cout<<c<<" "<<mp[c]<<endl;
if(!mp[c])mp[c]=++cnt;
if(typ)x=-x;
jz[i][mp[c]]+=x;
cin>>c;
if(c[0]=='=')typ=1;
if(c[0]=='H'&&c[1]=='='){
char a[3];
if(i==n+1){
cin>>c;
//cout<<c<<endl;
break;
}
cin>>hb[i];
//cin>>c;
break;
}
}
}
/*for(re k=1;k<=cnt;k++){
for(re i=1;i<=n+1;i++){
cout<<jz[k][i]<<" ";
}
cout<<hb[k]<<endl;
}*/
for(re i=1;i<=n;i++)jz[i][cnt+1]=hb[i];
int h,z=1;
for(h=1;h<=n&&z<=cnt;h++,z++){
int maxn=h;
for(re i=h+1;i<=n;i++)
if(fabs(jz[i][z])>fabs(jz[maxn][z]))
maxn=i;
if(maxn!=h)
for(re i=1;i<=cnt+1;i++)
swap(jz[maxn][i],jz[h][i]);
if(fabs(jz[h][z])==0){
h--;continue;
}
for(re i=h+1;i<=n;i++){
if(fabs(jz[i][z])==0)continue;
double t=jz[i][z]/jz[h][z];
for(re j=z;j<=cnt+1;j++)jz[i][j]-=jz[h][j]*t;
}
double t=jz[n+1][z]/jz[h][z];ans+=t*hb[h];
//cout<<h<<" "<<t<<" "<<hb[h]<<endl;
for(re j=z;j<=cnt+1;j++)jz[n+1][j]-=jz[h][j]*t;
}
//cout<<h<<" "<<z<<endl;
/*for(re i=h;i>=1;i--){
double t=jz[i][n+1];
for(re j=z;j>i;j--)
t-=jz[i][j]*x[j];
x[i]=t/jz[i][i];
}*/
/*for(re i=1;i<=n;i++){
ans+=x[i]*hb[i];
}*/
printf("%.1lf",(jz[n+1][cnt+1]==0)?0:-jz[n+1][cnt+1]);
}
·
T3 老司机的狂欢
这个题是真的恶心到我了,以下是我调这个题的过程:
刚开始做的时候,完全不能理解题意
半个小时之后,完全不知道该怎么做
然后考完了,我听他们讲,啥玩意树上倍增,我去你*的
刚开始改这个题,我只想拿部分分,然后想打树状数组优化的dp,发现我开不出来树状数组
15min later,我去问MAX,他说你就再离散化一边,我人傻了
10min later,我打出来了第一问,但是评测机不给分,我不服
10min later,我输出了个-1,部分分给我了
后来oj炸了,我就自己把spj改了改,加上我的bash,搞了个评测机,这个东西大大提高了我的改题效率
不久之后,我开始想正解,我直接想到,我能不能直接记录转移节点???
然后我就记录了,但是程序bug极多,改用离散化下标的没有用,改用id的也没用
1h later,我成功的调出来了,并且,我把样例过了。。。叫上去,仍然是60
1h later,我又仔细的调了调,拿到65分,但是后来一直高不上去了
然后此时已经过去了一上午。。。。。
下午来了,经过一中午的思考,我明白了,我不能保证每一个的最优转移节点就是最后的答案,因为完全有可能一个大的前面接个更小的
然后我开始打树上倍增,并没有把它运用到树状数组中,只利用它判断了最后一个人的前面,这样还是不能保证是最优解
然后下一场考试开始了
然后考完之后,我又想明白了,在树状数组的每一次转移都加上倍增判断,过掉了这个题
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define pa pair<int,int>
const int N=1e5+5;
int n,K;
struct CAR{
ll x,a;
int id;
}sca[N];
bool cmp(CAR x,CAR y){
return x.x<y.x;
}
ll nox[N],lsh[N];
struct sz_tree{
int tr[N];
int lb(int x){return x&(-x);}
void ins(int x,int v){
for(re i=x;i<=n;i+=lb(i))
tr[i]=max(tr[i],v);
}
int query(int x){
int ret=0;
for(re i=x;i;i-=lb(i))
ret=max(ret,tr[i]);
return ret;
}
void clear(){memset(tr,0,sizeof(tr));}
}sz;
int jt[N];
bool check(int tim){
for(re i=1;i<=n;i++){
nox[i]=sca[i].x*2+sca[i].a*tim*tim;
lsh[i]=nox[i];
}
sort(lsh+1,lsh+n+1);
for(re i=1;i<=n;i++){
nox[i]=lower_bound(lsh+1,lsh+n+1,nox[i])-lsh;
int tmp=sz.query(nox[i]-1)+1;
sz.ins(nox[i],tmp);
}
jt[tim]=sz.query(n);sz.clear();
if(jt[tim]>=K)return true;
else return false;
}
int fa[N][25],st[N][25];
void build(int x,int f){
fa[x][0]=st[x][0]=f;
for(re i=1;i<=20;i++){
fa[x][i]=fa[fa[x][i-1]][i-1];
st[x][i]=min(st[x][i-1],st[fa[x][i-1]][i-1]);
}
}
bool fcheck(pa x,pa y){
if(x.first!=y.first)return x.first<y.first;
int mx,my,ax,ay;
mx=ax=x.second;
my=ay=y.second;
for(re i=20;i>=0;i--){
if(fa[ax][i]!=fa[ay][i]){
mx=min(mx,st[ax][i]);
my=min(my,st[ay][i]);
ax=fa[ax][i];
ay=fa[ay][i];
}
}
return mx>my;
}
struct fsz_tree{
pa tr[N];
int lb(int x){return x&(-x);}
void ins(int x,pa v){
for(re i=x;i<=n;i+=lb(i))
if(fcheck(tr[i],v))
tr[i]=v;
}
pa query(int x){
pa ret=make_pair(0,0);
for(re i=x;i;i-=lb(i))
if(fcheck(ret,tr[i]))
ret=tr[i];
return ret;
}
}fsz;
int ji[N];
signed main(){
scanf("%d%d",&n,&K);
for(re i=1;i<=n;i++)scanf("%lld%lld",&sca[i].x,&sca[i].a),sca[i].id=i;
sort(sca+1,sca+n+1,cmp);
int l=0,r=86400;
while(l<r){
int mid=l+r+1>>1;
if(check(mid))l=mid;
else r=mid-1;
}
printf("%d\n",l);
if(jt[l]>K)printf("-1");
else{
for(re i=1;i<=n;i++)lsh[i]=nox[i]=sca[i].x*2+1ll*sca[i].a*l*l;
sort(lsh+1,lsh+n+1);
for(re i=1;i<=n;i++){
nox[i]=lower_bound(lsh+1,lsh+n+1,nox[i])-lsh;
pa tmp=fsz.query(nox[i]-1);
build(sca[i].id,tmp.second);
fsz.ins(nox[i],make_pair(tmp.first+1,sca[i].id));
}
int ans=fsz.query(n).second;
for(re i=1;i<=K;i++)ji[i]=ans,ans=fa[ans][0];
sort(ji+1,ji+K+1);
for(re i=1;i<=K;i++)printf("%d\n",ji[i]);
}
}
啊啊啊啊,真爽!!!!