Loading

NWERC 2020 部分题解

NWERC 2020 部分题解

A. Atomic Energy

题意

完全背包,物品的体积是\(1,2...n\) 价值是\(a_1,a_2...a_n\)

给出\(q\)次询问,每次询问体积恰为\(k\)的背包的价值是多少

\[1 \leq n \leq 100,q \leq 10^5\\ 1 \leq a_i \leq 10^9,1 \leq k \leq 10^9 \]

分析

发现背包的体积很大,物品的体积很小,能够明显地感觉到应该大范围贪心。

具体到哪个范围呢?考虑所有物品中性价比最高的物品体积\(m\),假设最后背包中的物品由体积为\(b_1,b_2...\)组成,可以得到结论若\(size(b) \geq m - 1\),可以把其中的物品用\(m\)替换为性价比最高的物品。这是因为大小为\(n\)的数组,总能找出子集使得和为\(n - 1\)(简证:对前缀和模n - 1,可以得到n个数至少有一对相同)。于是只需要做背包时把体积上限定为\(n \times (m - 1)\)即可

代码

这是赛时代码,事实上上限可以进一步逼近

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

int n, q;

const int N = 110, M = 5e5 + 10;
ll a[N];
ll f[M];

int main(){
    memset(f, 0x3f, sizeof f);
    scanf("%d%d",&n,&q);
    double mi = 1e9 + 1;
    int pos = -1;
    for (int i = 1; i <= n; i++) {
        scanf("%lld",&a[i]);
        double x = 1.*a[i] / i;
        if (x < mi) {
            mi = x;
            pos = i;
        }
    }
    assert(pos != -1);
    for (int i = 1; i <= n; i++) {
        f[i] = a[i];
    }
    constexpr int lim = 5e5;
    for (int i = n + 1; i <= lim; i++) {
        for (int j = 1; j <= n; j++) {
            f[i] = min(f[i], f[i - j] + a[j]);
        }
    }
    while (q--) {
        int k;
        scanf("%d",&k);
        if (k <= lim) printf("%lld\n",f[k]);
        else {
            int y = k - lim;
            int d = (y + pos - 1) / pos;
            long long ans = (long long)d * a[pos];
            k -= d * pos;
            ans += f[k];
            printf("%lld\n",ans);
        }
    }
    return 0;
}

C.Contest Struggles

水题

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;


//d * n = s * k + remain

inline int rd(){
	int x;
	scanf("%d",&x);
	return x;
}

int main(){
	int n = rd();
	int k = rd();
	int d = rd();
	int s = rd();
	double ans = (d * n - s * k) * 1.0 / (n - k);
   	if(ans >= 0 && ans <= 100) 	
		printf("%lf",(d * n - s * k) * 1.0 / (n - k));
	else puts("impossible");
}

D.Dragon Balls

题意

在2维平面的第一象限上(包括坐标轴)有\(n\)个点

你可以询问至多\(1000\)次,每次询问给出坐标\((x,y)\),每次会返回离该点的最近的点的距离的平方

点的范围\(0 \leq x \leq 10^6,0\leq y \leq 10^6\)

点的个数\(1 \leq n\leq 7\)

返回的距离\(0 \leq d \leq2\times 10^{12}\)

分析

尝试每次找原点,这样每次for圆弧上的整点,即可得到点的坐标

这样的前提是圆弧上的点不会很多,打表得到\(r\)较大时得到的点的个数不会很多

注意日经问题:开根号可能会有误差,不妨在map里预处理出所有开方数,每次check一下即可

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;


//d * n = s * k + remain

inline ll rd(){
	ll x;
	scanf("%lld",&x);
	return x;
}

ll get(int x,int y){
	printf("%d %d\n",x,y);
	fflush(stdout);
	ll g = rd();
	return g;
}


int main(){
	int n = rd();
	int cnt = 0;
	map<ll,bool> mp;
	for(int i = 0;i <= (int)1e6;i++)
		mp[(ll)i * i] = 1;
	while(cnt < n){
		ll d = get(0,0);
		if(!d) {
			cnt++;
			continue;
		}
		vector<pair<int,int>> v;
		for(ll x = 0;x * x <= d;x++){
			ll y = sqrt(d - x * x);
			if(!mp.count(d - x * x)) continue;
			v.push_back(make_pair(x,y));
		}

		for(int i = 0;i < v.size();i++){
			ll d = get(v[i].first,v[i].second);
			if(!d) {
				cnt++;
				break;
			}
		}
	}
}

E.Endgame

题意

\(n \times n\)的棋盘上两人进行移动。每次两人可以选择如下操作之一:1.进行两次给定的移动。2.移动到任意没有棋子的位置3.不移动

共有\(n\)个给定移动,负数表示向左或者向上,不能移动到界外

输出Alice或者Bob或者平局,若平局输出某个情况下的Alice的位置

\(2 \leq n \leq 10^5\)

分析

考虑给定的移动只有\(n\)次移动具有什么性质,得到每次只能移动到至多\(n^2/2\)个位置

Alice如果不能在第一次就在两步内移动到Bob,那么Alice不会赢(由于有任意移动的操作)

所以我们主要考察初始状态即可,再结合\(n^2/2\)的性质,说明随机在棋盘上选定一个点,它在可以移动的位置的概率最多是\(1/2\),如果选\(k\),次,一次都没有选到不可达位置的概率是\((1/2)^k\) ,因此上随机化就行了(之前的题的思想)

已知棋盘上两点,如何判断能否在两点内可达。只须考虑\(meet \ in \ middle\)的思想就可以做到\(O(nlogn)\)

因此如果局面是\(tie\),就可以在\(100\)次左右随机到答案,否则输出Bob

代码

#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define eps 1e-9
#define db long double
#define equals(a,b) fabs(a-b) < eps
using namespace std;

typedef long long ll;



inline ll rd(){
	ll x;
	scanf("%lld",&x);
	return x;
}

const int maxn = 4e5 + 5;

mt19937 rnd(time(0));
map<pii,bool> mp;
pii op[maxn];
int n;

bool ok(int dp,int dv,int p,int v){
	if(!dp && !dv) return 1;
	if(mp[make_pair(dp,dv)]) return 1;
	for(int i = 1;i <= n;i++){
		int uu = p + op[i].fi;
		int vv = v + op[i].se;
		if(uu <= 0 || uu > n || vv <= 0 || vv > n) continue;
		if(mp[make_pair(dp - op[i].fi,dv - op[i].se)]) return 1;
	}
	return 0;
}


int main(){
	n = rd();
	int stx,sty;
	int edx,edy;
	stx = rd(),sty = rd();
	edx = rd(),edy = rd();
	for(int i = 1;i <= n;i++){
		int dx = rd();
		int dy = rd();
		auto p = make_pair(dx,dy);
		mp[p] = 1;
		op[i] = p;
	}
	if(ok(edx - stx,edy - sty,stx,sty)) {
		puts("Alice wins");
		return 0;
	}
	int times = 500;
	while(times--){
		int i = rnd() % n + 1,j = rnd() % n + 1;
		if(!ok(i - edx,j - edy,edx,edy)) {
			printf("tie %d %d\n",i,j);
			return 0;
		}
	}
	puts("Bob wins");
}

F.Flight Collision

题意

一维坐标上飞机具有\(x_i\)坐标和速度\(v_i\),其中速度是矢量

若两飞机相遇则会同时消失,问最后留下的飞机的编号

分析

观察到性质每次相撞的飞机总是前一时间相邻的飞机

每次当前局面下第一次相撞的飞机是距离除以相对速度最小的飞机

因此可以维护当前和后继的相遇时间,每次pop出来后删掉即可

维护前驱后继可以用链表,也可以用set(因为前驱后继一定有偏序关系)

实现起来可能有点麻烦,这里用pop需要处理一下之前留下的没用的结点,遇到是时候注意continue掉就好了

代码

#include<bits/stdc++.h>
#define pii pair<db,pair<int,int>>
#define fi first
#define se second
#define eps 1e-9
#define db long double
#define equals(a,b) fabs(a-b) < eps
using namespace std;

typedef long long ll;


//d * n = s * k + remain

inline ll rd(){
	ll x;
	scanf("%lld",&x);
	return x;
}

const int maxn = 1e5 + 5;

int x[maxn],v[maxn];
priority_queue<pii> Q;

void add(int p,int q){
	if(v[p] <= v[q]) return;
	db T = (db)(x[p] - x[q]) / (db)(v[p] - v[q]); 
	Q.push(make_pair(T,make_pair(p,q)));
}


int main(){
	set<int> s;
	int n = rd();
	for(int i = 1;i <= n;i++){
		x[i] = rd();
		v[i] = rd();
		s.insert(i);
	}
	for(int i = 1;i < n;i++)
		add(i,i + 1);
	while(!Q.empty() && s.size()){
		auto P = Q.top().se;
		int i = P.fi;
		int j = P.se;
		Q.pop();
		if(!s.count(j) || !s.count(i)) continue;
		s.erase(i);
		s.erase(j);
		if(!s.size()) break;
		auto it = s.lower_bound(i);
		if(it == s.end() || it == s.begin()) continue;
		int pre = *(--it);
		int nxt = *s.lower_bound(j);
		add(pre,nxt);
	}
	printf("%d\n",s.size());
	for(auto it:s){
		cout << it << '\n';
	}
}

H.Hot Springs

题意

给定\(t_1,t_2...t_n\)重新排列使得\(|t'_{i-1} - t'_{i+1}| \leq |t'_i - t'_{i+1}|\)

分析

这种题图画出来就很容易发现该如何构造了

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;


//d * n = s * k + remain

inline int rd(){
	int x;
	scanf("%d",&x);
	return x;
}

int main(){
	int n = rd();
	vector<int> v(n);
	for(int i = 0;i < n;i++){
		v[i] = rd();
	}
	sort(v.begin(),v.end());
	if(n == 2) {
		cout << v[0] << ' ' << v[1];
		return 0;
	}
	if(n == 3) {
		cout << v[1] << ' ' << v[0] << ' ' << v[2];
		return 0;
	}
	vector<int> ans;
	if(abs(v[0] - v[n - 2]) > abs(v[1] - v[n - 1])) {
		ans.push_back(n - 1);
		ans.push_back(0);
		ans.push_back(n - 2);
		int l = 1,r = n - 3;
		for(int i = 0;i < n - 3;i++){
			if(!(i & 1)) ans.push_back(l++);
			else ans.push_back(r--);
		}
	}
	else {
		ans.push_back(0);
		ans.push_back(n - 1);
		ans.push_back(1);
		int l = 2,r = n - 2;
		for(int i = 0;i < n - 3;i++){
			if(i & 1) ans.push_back(l++);
			else ans.push_back(r--);
		}
	}
	reverse(ans.begin(),ans.end());
	for(auto it:ans){
		cout << v[it] << ' ';
	}
}

I.Island Tour

队友写的

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=405;
int d[maxn];
int n;
int t[4][maxn];
struct node{
    int l,r;
}s[4][405][405];

vector<int>ab[405];
vector<int>ac[405];
vector<int>bc[405];

map<pair<int,int>,int>mp;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&d[i]);
    }
    for(int j=1;j<=3;j++)
    for(int i=1;i<=n;i++){
        scanf("%d",&t[j][i]);
    }

    for(int person=1;person<=3;person++){
        for(int i=1;i<=n;i++){
            int now=1;
            int cnt=0;
            for(int j=i;j<=n;j++){
                s[person][i][j]=node{now,now+t[person][j]-1};
                now=now+t[person][j]-1;
                now=now+d[j]+1;
            }
            for(int j=1;j<i;j++){
                s[person][i][j]=node{now,now+t[person][j]-1};
                now=now+t[person][j]-1;
                now=now+d[j]+1;
            }
        }
        /*
        for(int i=1;i<=n;i++){
            cout<<i<<endl;
            for(int j=i;j<=n;j++){
                cout<<s[person][i][j].l<<" "<<s[person][i][j].r<<"xxx";
            }
            for(int j=1;j<i;j++){
                cout<<s[person][i][j].l<<" "<<s[person][i][j].r<<"xxx";
            }
            cout<<endl;
        }
        */
    }


    // a b

    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int kk=1;
            for(int k=1;k<=n;k++){
                if(!(s[1][i][k].l>s[2][j][k].r||s[2][j][k].l>s[1][i][k].r)){
                    kk=0;
                }
            }
            if(kk)ab[i].push_back(j);
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int kk=1;
            for(int k=1;k<=n;k++){
                if(!(s[1][i][k].l>s[3][j][k].r||s[3][j][k].l>s[1][i][k].r)){
                    kk=0;
                }
            }
            if(kk)ac[i].push_back(j);
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int kk=1;
            for(int k=1;k<=n;k++){
                if(!(s[2][i][k].l>s[3][j][k].r||s[3][j][k].l>s[2][i][k].r)){
                    kk=0;
                }
            }
            if(kk)bc[i].push_back(j);
        }
    }

    /*
    for(int i=1;i<=n;i++){
        for(auto x:ab[i]){
            cout<<i<<" "<<x<<endl;
        }
    }

    for(int i=1;i<=n;i++){
        for(auto x:ac[i]){
            cout<<i<<" "<<x<<endl;
        }
    }

    for(int i=1;i<=n;i++){
        for(auto x:bc[i]){
            cout<<i<<" "<<x<<endl;
        }
    }


    */
    for(int i=1;i<=n;i++){
        for(auto x:bc[i]){
            mp[{i,x}]=1;
        }
    }

    for(int i=1;i<=n;i++){
        for(auto x:ab[i]){
            for(auto y:ac[i]){
                if(mp[{x,y}]){
                    cout<<i<<" "<<x<<" "<<y<<endl;
                    return 0;
                }
            }
        }
    }
    cout<<"impossible";
}

K.Keyboardd

水题

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;


//d * n = s * k + remain

inline int rd(){
	int x;
	scanf("%d",&x);
	return x;
}

map<char,int>mp;
int main(){
	string s,t;
	getline(cin,s);
	getline(cin,t);
	int now=1;
	int p=0;
	for(int i=1;i<t.size();i++){
        if(t[i]==t[i-1]){
            now++;
        }else{
            int pp=0;
           for(int j=p;j<s.size();j++){
             if(s[j]==t[i-1])pp++;
             else {
                p=j;
                break;
             }
           }
           if(pp==now){
             now=1;
           }else{
             if(mp[t[i-1]]!=1)cout<<t[i-1];
             mp[t[i-1]]=1;
             now=1;
            }
        }
	}

	if(now!=(s.size()-p)){
        if(mp[s[p]]!=1)cout<<s[p];
	}
}
posted @ 2021-07-11 19:43  MQFLLY  阅读(876)  评论(0编辑  收藏  举报