QZS8.19

T1

过不了大样例

查不出错。。。可能想错了

wawawa爆零了

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int N=500005;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
inline void Min(int &x,int y) {if(x>y)x=y;}
inline void Max(int &x,int y) {if(x<y)x=y;}
int jue(int x,int y) {return x>y?x-y:y-x;}
vector<int>v[N];
int n,k,fa[N],ban[N];
int to[N],nxt[N],hd[N],w[N],tot;
inline void add(int x,int y,int z) {
    to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
}

int dep[N],dis[N],mxdep;
int mi[N];
void dfs(int x) {
    for(int i=hd[x];i;i=nxt[i]) {
        int y=to[i];
        if(y==fa[x]) continue;
        dep[y]=dep[x]+1;
        v[dep[y]].push_back(y);
        Max(mxdep,dep[y]);
        dis[y]=dis[x]+w[i];
        Min(mi[dep[y]],dis[y]);
        dfs(y);
    }
}   
int main() {
    memset(mi,0x3f,sizeof(mi));
    n=read();k=read();
    for(int i=2,z;i<=n;i++) {
        fa[i]=read();z=read();ban[i]=read();
        add(fa[i],i,z);
    }   
    dep[1]=0;mi[0]=0;
    v[0].push_back(0);
    dfs(1);
    puts("0");
    for(int s=1;s<=mxdep;s++) {
        for(int j=0;j<v[s].size();j++) {
            if(!ban[v[s][j]]) 
                for(int last=0;last<=mxdep;last++) 
                    Min(dis[v[s][j]],mi[last]+jue(s,last)*k);
            Min(mi[s],dis[v[s][j]]);
        } 
    }
    for(int i=2;i<=n;i++) 
        printf("%d\n",dis[i]);
    return 0;
}

不知所错。。。

去撸正解吧


额怎么说呢。。。想的又和正解挺像的

就是每层开vector 存一个当前层最小值

有一个点没有想到

就是可以记录一个bestpos最优点,它后面的层都可以用它更新,正确性证明(口糊):

我们按层数从小到大推,设之前的bestpos为 i ,新的bestpos为 j (i到1的距离>j到1的距离) ,那么对后面的层 k 来说 dep[k]-dep[i] > dep[k]-dep[j] && i到1的距离>j到1的距离 ∴ 之后跳 j 一定比跳 i 优

还有个小优化 输入边权的时候如果能坐车就和k取min

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int N=500005;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
inline void Min(int &x,int y) {if(x>y)x=y;}
inline void Max(int &x,int y) {if(x<y)x=y;}
vector <int> v[N];
int n,k,fa[N],ban[N];
int to[N],nxt[N],hd[N],w[N],tot;
inline void add(int x,int y,int z) {
    to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
}

int dep[N],dis[N],mxdep;
int best[N],bestpos;
int get(int x,int y) {return best[x]+(y-x)*k;}
int main() {
    memset(best,0x3f,sizeof(best));//每一层的最小值
    n=read();k=read();
    for(int i=2,z;i<=n;i++) {
        fa[i]=read();z=read();ban[i]=read();
        dep[i]=dep[fa[i]]+1;
        Max(mxdep,dep[i]);
        if(!ban[i]) Min(z,k);//优化
        add(fa[i],i,z);
    }   
    for(int i=1;i<=n;i++) v[dep[i]].push_back(i); 
    for(int i=0;i<=mxdep;i++) {//这个循环是第i层已经完成,在第i层推向第i+1层
        for(int j=0;j<v[i].size();j++) {
            int y=v[i][j];
            Min(best[dep[y]],dis[y]);
        }
        int mi=best[i];
        for(int j=0;j<v[i].size();j++)
            if(!ban[v[i][j]])
                Min(dis[v[i][j]],mi);
        if(i&&get(bestpos,i)>get(i,i)) bestpos=i;
        for(int j=0;j<v[i].size();j++) {
            int x=v[i][j];
            for(int s=hd[x];s;s=nxt[s]) {
                int y=to[s];
                dis[y]=dis[x]+w[s];
                if(!ban[y]) Min(dis[y],get(bestpos,i+1));
            }
        }
    }
    for(int i=1;i<=n;i++) 
        printf("%d\n",dis[i]);
    return 0;
}
/*
4 3
1 7 1
1 5 0
2 1 0
*/

T2

Solution

把原来的pos排序,二分时间 t ,然后更新成新的pos’ ,再排序比较二者顺序是否完全相同。

疑难:对k不会处理 60 pts

std: 其实就差一点想到正解。。。我们对新的pos’ 求出最长上升子序列LIS , 然后 若序列长度>=n-k 就可行

(n^2暴力枚举任意两个点相撞时间,取min可以90分。。。痛心)


60分

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=50005;
int n,k;
double ans;
struct node{
     double p,v;int id,rk;
}a[N],b[N];
bool cmp(node a,node b) {
    return a.p==b.p?a.id<b.id:a.p<b.p;
}
bool check(double t) {
    for(int i=1;i<=n;i++) {
        b[i]=a[i];
        b[i].p=a[i].p+a[i].v*t;
    }
    sort(b+1,b+1+n,cmp);
    for(int i=1;i<=n;i++)
        if(b[i].rk!=i) return 0;
    return 1;
}
int main() {
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) {
        scanf("%lf%lf",&a[i].p,&a[i].v);
        a[i].id=i;
    }
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)
        a[i].rk=i;
    
    double l=0,r=1000000;
    while(l<=r) {
        double mid=(l+r)/2.0;
        if(check(mid)) ans=mid,l=mid+0.00001;
        else r=mid-0.00001;
    }       
    if(ans>=999999) puts("Forever");
    else printf("%.4lf",ans);
    return 0;
}

100分

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long double ld;
const int N=1e5+5;
const ld eps=1e-4;
const ld inf=1e12;
int n,k;
struct node{
    ld p,v;
    int id;
    bool operator < (const node &x) const {
        return p<x.p;
    }
}a[N],b[N];
inline void Max(int &x,int y) {if(x<y)x=y;}
int c[N];
int query(int x) {
    int res=0;
    for(int i=x;i;i-=i&(-i))
        Max(res,c[i]);
    return res;
}
void upd(int x,int v) {
    for(int i=x;i<=n;i+=i&(-i))
        Max(c[i],v);   
}
inline bool check(ld t) {
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;++i)
        b[i].p=a[i].p+a[i].v*t,b[i].id=i;
    sort(b+1,b+n+1);
    for(int i=1;i<=n;++i)
        a[b[i].id].id=i;
    for(int i=1;i<=n;++i) {
        int x=a[i].id;
        int y=query(x-1);
        upd(x,y+1);
    }
    return query(n)>=n-k;
}
int main() {
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)
        scanf("%Lf%Lf",&a[i].p,&a[i].v);
    sort(a+1,a+n+1);
    ld l=0,r=inf,mid;
    while(abs(l-r)>eps*eps){
        mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    if(abs(mid-inf)<=eps) puts("Forever");
    else printf("%.4Lf",mid);
    return 0;
}


T3

不会处理n>1

好像斜率优化啊


首先,一个结论是,对于长度为 len 的一段,切 k 次,代价最小的切法是尽量平均地切,也就是我们要切出长度为 len/k的段一共 k-x mod k 段,长度为 len/k+1的段一共 x mod k 段

令 cost(len,k) 为长度为len,切k刀的最小代价。

注意到其差分序列是不降的——意思解释你越往下切,代价减少的越慢。

栗子:
cost(8,0) = 64
cost(8,1) = 32 降32
cost(8,3) = 16 降16

image-20200822172117845

问题可以形象的转化为上图,每个格子值为代价差 \(cost(len,k+1)-cost(len,k)\)

每一列是一个饼,每一行的值都小于0,且从上到下绝对值递减

多切1刀的话就取堆顶。问题转化成找一条包含k个格子的轮廓线。

总体复杂度O(qnlog)的,45分

对于每个询问有的只是k+1,k-1,你要是再重新求一遍显然不划算。

但堆只允许我们往下拓展取格子,不能还原回去

由于要支持删除,我们用 \(multiset\) 而不用堆

考虑如何反悔:
我们维护两个 \(multiset\) ,一个维护切,一个维护反悔
两个 \(multiset\) 按照差从小到大(因为是负数)自动排序
\(k\) 增加的时候,取切的顶加,同时由于 \(k\) 增加了,原来的 \(k\) 的时候对应的反悔就失效了,于是删除那个反悔,以及原来的切,同时加入新的切和反悔
\(k\) 减小的时候取反悔的顶加,同时旧的切和反悔失效,删掉,加入新的切和反悔

对于加入每一个元素,初始放入 \(multiset\) 时,在切的set里放入 \(cost(len,2)-cost(len,1)\) ,在反悔里放入 \(cost(len,0)-cost(len,1)\)
记录每一个元素已经被分为几段,初始时为1

对于加入新的饼也是类似的,但如果你发现你反悔后再加入的不是新的饼的贡献,说明这加的新饼没用

#include <iostream>
#include <cstdio>
#include <set>
using namespace std;
#define int long long 
const int inf=8e18; 
const int N=1000005; 
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,q,k,a[N],d[N],ans;//d[i]表示i号pancake切了几刀
int get(int x,int k) {
    if(k==0||x<k) return inf;
    int len=x/k;
    return len*len*(k-x%k)+(len+1)*(len+1)*(x%k);
}
struct node{
    int pos,val;
    node(int a,int b):pos(a),val(b){}
    bool operator < (const node &x) const {
        return val==x.val?pos<x.pos:val<x.val;
    }
};
multiset<node>s0,s1;
void add() {
    multiset<node>::iterator it=s0.begin();
    ans+=it->val;
    s1.erase(node(it->pos,get(a[it->pos],d[it->pos]-1)-get(a[it->pos],d[it->pos])));
    s0.erase(*it);
    ++d[it->pos];
    s0.insert(node(it->pos,get(a[it->pos],d[it->pos]+1)-get(a[it->pos],d[it->pos])));
    s1.insert(node(it->pos,get(a[it->pos],d[it->pos]-1)-get(a[it->pos],d[it->pos])));
}
void del() {
    multiset<node>::iterator it=s1.begin();
    ans+=it->val;
    s0.erase(node(it->pos,get(a[it->pos],d[it->pos]+1)-get(a[it->pos],d[it->pos])));
    s1.erase(*it);
    --d[it->pos];
    s0.insert(node(it->pos,get(a[it->pos],d[it->pos]+1)-get(a[it->pos],d[it->pos])));
    s1.insert(node(it->pos,get(a[it->pos],d[it->pos]-1)-get(a[it->pos],d[it->pos])));
}
signed main() {
    n=read();q=read();k=read();
    for(int i=1;i<=n;i++) {
        a[i]=read();
        ans+=a[i]*a[i];
        d[i]=1;
        s0.insert(node(i,get(a[i],2)-get(a[i],1)));
        s0.insert(node(i,get(a[i],0)-get(a[i],1)));
    }
    for(int i=1;i<=k;i++)
        add();
    printf("%lld\n",ans);
    for(int i=1,op;i<=q;i++) {
        op=read();
        if(op==1) add();
        else if(op==2) del();
        else {
            n++;
            a[n]=read();
            d[n]=1;
            ans+=a[n]*a[n];
            s0.insert(node(n,get(a[n],2)-get(a[n],1)));
            s1.insert(node(n,get(a[n],0)-get(a[n],1)));
            int last=ans;
            while(1) {
                del();
                add();
                if(last==ans) break;
                last=ans;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
/*
1 3 1
4 
1 
3 4
2
*/
posted @ 2020-08-22 19:02  ke_xin  阅读(23)  评论(0编辑  收藏  举报