2019-2020 ICPC Northwestern European Regional Programming Contest (NWERC 2019)

\(A. Average Rank\)

将每个人的排名看作是前面一个人的贡献,然后采用类似懒标记的形式优化复杂度。

int sum[N],point[N],cnt[N],pre[N],laz[N];
void solve(){
    int n=read(),w=read();
    laz[0]=w;
    cnt[0]=n;
    for(int i=0;i<w;i++){
        int k=read();
        for(int j=0;j<k;j++){
            int x=read();
            int &p=point[x];
            sum[x]+=laz[p]-cnt[p+1]*(w-i)-pre[x];  
            laz[p]+=w-i;   
            --cnt[p];  
            ++cnt[++p];
            pre[x]=laz[p];
        }
    }
    for(int i=1;i<=n;i++){
        int p=point[i];
        sum[i]+=laz[p]-pre[i];
        double ans=sum[i]*1.0/w;
        printf("%.10lf\n",ans);
    }   
    //puts(ans>0?"YES":"NO");
    //puts(ans>0?"Yes":"No");
}

\(D. Disposable Switches\)

所有边边权 \(*u\) 。考虑若最短路经过 \(k\) 条边,可用一次函数表达,这时候所有一次函数构成的图的下沿就是可能的一些路径,用 \(floyed\) 完成 \(dp\) 过程。

int n,m,dis[N][N];
vector<PII>G[N];
bool res[N],vis[N][N];
bool cross(PII a, PII b, PII c) {
	double xc=c.second-a.second;
	double yc=a.first-c.first;
	double xb=b.second-a.second;
	double yb=a.first-b.first;
	return xc*yb<xb*yc;
}
void Floyd() {
	for(int i=0;i<=n;i++){
		for(int j=0;j<=n;j++){
			dis[i][j]=INF;
		}
	}
	dis[0][1]=0;
	for(int i=0;i<=n-2;i++){
		for(int j=1;j<=n;j++){
			for(auto x:G[j]){
				dis[i+1][x.first]=min(dis[i+1][x.first],dis[i][j]+x.second);
			}
		}
	}
}
void dfs(int u,int v){
	res[v]=vis[u][v]=true;
	if(!u)return ;
	for(auto x:G[v]){
		if(dis[u-1][x.first]+x.second==dis[u][v]){
			if(vis[u-1][x.first])continue;
			dfs(u-1,x.first);
		}
	}
}
void solve() {
    n=read(),m=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read(),c=read();
		G[u].push_back(make_pair(v,c));
		G[v].push_back(make_pair(u,c));
	}
	Floyd();
	vector<PII>st;
	for(int i=n-1;i>=1;i--){
		auto x=make_pair(i,dis[i][n]);
		while(st.size()>=2){
			if(cross(st[st.size()-2],st[st.size()-1],x)) st.pop_back();
			else break;
		}
		if(st.size()==1&&st.back().second>x.second)st.pop_back();
		st.push_back(x);
	}
	for(auto x:st){
		dfs(x.first,n);
	}
	vector<int>ans;
	for(int i=1;i<=n;i++){
		if(res[i])continue;
		ans.push_back(i);
	}
	cout<<ans.size()<<'\n';
	for(auto x:ans){
		cout<<x<<' ';
	}
}

\(H. Height Profile\)

把分数化为\(a_i-g*i \le a_j-g*j\),然后排序计算答案。
从小到大的排序后,我们只需要找到一个左右能成立的点(在已排除不合法的情况下必然存在答案),同时判断能否向两端延伸。

int h[N];
void solve(){
    int n=read(),k=read(),maxx=0;
    for(int i=0;i<=n;i++){
        h[i]=read();
    }
    for(int i=1;i<=n;i++){
        maxx=max(maxx,h[i]-h[i-1]);
    }
    for(int i=1;i<=k;i++){
        double g;
        cin>>g;
        int x=round(g*10.0);
        if(maxx<x){
            cout<<"-1\n";
            continue;
        }else{
            double ans=-1;
            vector<PII>vec(n+1),tmp(n+1);
            for(int ii=0;ii<=n;ii++){
                vec[ii]=make_pair(h[ii]-ii*x,ii);
                tmp[ii]=vec[ii];
            }
            sort(vec.begin(),vec.end());
            int l=n,r;
            for(auto [x,y]:vec){
                r=y;
                if(l<r){
                    double res=0;
                    if(l>0)res=max(res,(double)(tmp[r].first-tmp[l].first)/fabs(tmp[l].first-tmp[l-1].first));
                    if(r<n)res=max(res,(double)(tmp[r].first-tmp[l].first)/fabs(tmp[r+1].first-tmp[r].first));
                    if(res>1.0)res=1.0;
                    ans=max(ans,r-l+res);
                }
                l=min(l,r);
            }
            printf("%.12lf\n",ans);
        }
    }
    //puts(ans>0?"YES":"NO");
    //puts(ans>0?"Yes":"No");
}

\(J. Jackdaws And Crows\)

题目:给定一个长度为 \(n\) 的数组 \(a\) 和两个参数 \(r,c\) ,你可以使用 \(r\) 秒时间删除数组中的一个数字,或者使用 \(c\) 秒时间将数组中的任意个数字加一或减一。询问将数组变成相邻异号的数组所需的最少时间。

题解:还没看懂,先贴个 \(jiangly\) 慢慢悟。

#include <bits/stdc++.h>
struct Matrix {
    int a00, a01, a10, a11;
};
Matrix operator*(const Matrix &lhs, const Matrix &rhs) {
    Matrix res;
    res.a00 = std::min(lhs.a00 + rhs.a00, lhs.a01 + rhs.a10);
    res.a01 = std::min(lhs.a00 + rhs.a01, lhs.a01 + rhs.a11);
    res.a10 = std::min(lhs.a10 + rhs.a00, lhs.a11 + rhs.a10);
    res.a11 = std::min(lhs.a10 + rhs.a01, lhs.a11 + rhs.a11);
    return res;
}
struct SegmentTree {
    int n;
    std::vector<Matrix> t;
    SegmentTree(int n) : n(n), t(4 * n) {
        build(1, 0, n);
    }
    void build(int p, int l, int r) {
        t[p].a00 = t[p].a11 = r - l;
        t[p].a01 = t[p].a10 = 1e9;
        if (r - l == 1)
            return;
        int m = (l + r) / 2;
        build(2 * p, l, m);
        build(2 * p + 1, m, r);
    }
    void modify(int p, int l, int r, int x, int y) {
        if (r - l == 1) {
            if (y == 0) {
                t[p].a10 = 0;
            } else {
                t[p].a01 = 0;
            }
            return;
        }
        int m = (l + r) / 2;
        if (x < m) {
            modify(2 * p, l, m, x, y);
        } else {
            modify(2 * p + 1, m, r, x, y);
        }
        t[p] = t[2 * p] * t[2 * p + 1];
    }
    void modify(int x, int y) {
        modify(1, 0, n, x, y);
    }
    int get() {
        return std::min({t[1].a00, t[1].a01, t[1].a10, t[1].a11});
    }
};
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int n, c, r;
    std::cin >> n >> c >> r;
    int64_t ans = 1ll * n * r;
    SegmentTree t(n);
    std::vector<std::tuple<int, int, int>> events;
    events.reserve(2 * n);
    for (int i = 0; i < n; ++i) {
        int x;
        std::cin >> x;
        events.emplace_back(std::max(0, 1 - x), i, 0);
        events.emplace_back(std::max(0, 1 + x), i, 1);
    }
    std::sort(events.begin(), events.end());
    for (auto [a, x, y] : events) {
        t.modify(x, y);
        ans = std::min(ans, 1ll * a * c + 1ll * t.get() * r);
    }
    std::cout << ans << "\n";
    return 0;
}
posted @ 2023-10-17 00:26  EdGrass  阅读(33)  评论(1)    收藏  举报