1. 程式人生 > 其它 >NWERC 2020 部分題解

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];
	}
}