2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)

2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)

A: Airport Coffee

一个人要从起点走到终点,距离为\(d\),速度恒\(a\)
中间有一些咖啡馆,当经过咖啡馆买了咖啡后,速度会提高
变成\(b\),具体点就是买了咖啡后持续\(t\)时间保持速度\(a\),之后
持续\(r\)保持速度\(b\),最后速度又变回\(a\),每个咖啡馆最多只能买一杯咖啡,咖啡在中途可以扔掉再买新的,问从起点到终点的最短时间,输出在哪些咖啡馆买过咖啡,任意方案只要是最短时间即可。

思路:

定义\(dp[i]\)表示从第\(i\)个咖啡馆出发买了咖啡到达终点的最短时间
\(dp[i] = min(dp[j] + cost(i,j))\)
暴力枚举复杂度为\(O(n ^ {2})\)
注意到\(dp[i] 从 1到 i\) 一定是递减的
且第\(i\)个点买了咖啡之后 可以分为三个阶段
速度为 \(a\),速度为\(b\),速度再次为\(a\)
第一个阶段和第三个阶段肯定选最接近\(i\)\(j\),第二个阶段肯定选最后的\(j\),所以其实只需要二分即可。

#include<bits/stdc++.h>
#define P pair<int,int>
#define LL long long

using namespace std;

const LL INF = 1e18;
const int N = 5e5 + 10;
const double eps = 1e-10;
LL d,a,b,t,r,n;
LL cof[N];
int nxt[N];
double f[N];
double cal(double x){
    if(x <= a * t) return x / a;
    if(x <= a * t + b * r) return t + (x - a * t)  / b;
    return t + r + (x - a * t - b * r) / a;
}
int main(){

   cin>>d>>a>>b>>t>>r;
   cin>>n;
   if(!n) {
    cout<<0<<endl;
    return 0;
   }
   for(int i = 0;i < n;i++){
        scanf("%lld",cof + i);
        f[i] = INF;
   }
   int flag = 0;
   if(cof[n - 1] != d) flag = 1,cof[n++] = d;
   else f[n - 1] = 0;
   nxt[n - 1] = -1;
   for(int i = n - 2;i >= 0;i--){
        int pos = lower_bound(cof, cof + n, cof[i]+1) - cof;
        if(cof[pos] - cof[i] - a * t < -eps){
            double tmp = f[pos] + cal(cof[pos] - cof[i]);
            if(tmp  - f[i]< -eps){
                nxt[i] = pos,f[i] = tmp;
            }
        }
        pos = upper_bound(cof, cof + n, cof[i] + a * t + b * r) - cof;
        if(pos < n){
           double tmp = f[pos] + cal(cof[pos] - cof[i]);
            if(tmp - f[i] < -eps){
                nxt[i] = pos,f[i] = tmp;
            }
        }
        if(a * t - cof[--pos] + cof[i] < -eps){
            double tmp = f[pos] + cal(cof[pos] - cof[i]);
            if(tmp - f[i] < -eps){
                nxt[i] = pos,f[i] = tmp;
            }
        }
   }
   
   int u = 0;
   vector<int> res;
   while(u != -1){
        res.push_back(u);
        u = nxt[u];
   }
   if(*(res.end()-1) == n - 1 && flag) res.erase(res.end()-1);
   cout<<res.size()<<endl;
   if(res.size()){
    for(int i = 0 ;i < res.size();i++) cout<<res[i]<<" ";
    cout<<endl;
   }
   return 0;
}

C: Compass Card Sales

在一个罗盘上给出\(n\)张卡片\((r,g,b,id)\)
\(0 <= r,g,b < 360,0 <= id < 2^{31}\)
定义卡片\(i\)\(unique\)分数为\(x_l + x_r + y_l + y_r + z_l + z_r\)
\(x_l\)表示逆时针走最接近\(r_i\)\(r\)的差,\(x_r\)表示顺时针走最接近\(r_i\)\(r\)的差
当有两个相同的\(r_i\)时,\(x_l和x_r\)等于0

y和z同理

\(当卡片的unique分数相同时,id越大的越小\)

每次输出unique分数最小的卡片id,将它从罗盘上删去,然后更新其他卡片的分数,直到所有的卡片都删除。

思路:

丢到set中,每次删除一个卡片,最多只会对左右两边有影响,乱搞就好了,代码简直写的不能再乱了。

#include<bits/stdc++.h>
#define P pair<int,int>
#define LL long long

using namespace std;

const int N = 1e5 + 10;
struct node{
    int score,id;
    node(){};
    node(int score,int id):score(score),id(id){};
    bool operator<(const node &rhs)const{
        if(score != rhs.score) return score < rhs.score;
        return id > rhs.id;
    }

};
map<int,int> seq;
set<node> se;
int tmp[N];
int ele[N][4];
int vis[3][400];
set<int> res[3][400];
vector<int> angle[3];
int l[3][400],r[3][400],sc[3][400];///逆时针,顺时针
int calc_ang(int c,int x){
    if(vis[c][x] > 1) return 0;
    int ang = 0,ll = l[c][x],rr = r[c][x];
    ang += ll < x?x - ll:360 + x - ll;
    ang += rr > x?rr - x:360 + rr - x;
    return ang;
}
int cal(int x){
    int ans = 0;
    for(int i = 0;i < 3;i++) ans += sc[i][ele[x][i]];
    return ans;
}
void update(int x){

   for(int i = 0;i < 3;i++){
        int ang = ele[x][i];
        if(--vis[i][ang] == 0){
            int ll = l[i][ang],rr = r[i][ang];
            l[i][rr] = ll, r[i][ll] = rr;
            int tp = calc_ang(i,ll);
            if(sc[i][ll] != tp){
                sc[i][ll] = tp;
                for(auto v:res[i][ll]){
                    auto it = se.find(node(tmp[v],ele[v][3]));
                    se.erase(it);
                    tmp[v] = cal(v);
                    se.insert(node(tmp[v],ele[v][3]));
                }
            }
            tp = calc_ang(i,rr);
            if(sc[i][rr] != tp){
                sc[i][rr] = tp;
                for(auto v:res[i][rr]){
                    auto it = se.find(node(tmp[v],ele[v][3]));
                    se.erase(it);
                    tmp[v] = cal(v);
                    se.insert(node(tmp[v],ele[v][3]));
                }
            }
        }
        if(vis[i][ang] == 1){
            for(auto v:res[i][ang]){
                sc[i][ang] = calc_ang(i,ang);
                if(tmp[v] != cal(v)){
                    se.erase(se.find(node(tmp[v],ele[v][3])));
                    se.insert(node(tmp[v]=cal(v),ele[v][3]));
                }
            }
        }
   }
}
int main(){

   int n;
   cin>>n;
   for(int i = 1;i <= n;i++){
        for(int j = 0;j < 4;j++) scanf("%d",&ele[i][j]);
        for(int j = 0;j < 3;j++) {
                if(!vis[j][ele[i][j]]) angle[j].push_back(ele[i][j]);
                res[j][ele[i][j]].insert(i);
                vis[j][ele[i][j]]++;
        }
        seq[ele[i][3]] = i;
   }
   for(int i = 0;i < 3;i++){
        sort(angle[i].begin(),angle[i].end());
        for(int j = 0; j + 1 < angle[i].size();j++){
            l[i][angle[i][j + 1]] = angle[i][j];
            r[i][angle[i][j]] = angle[i][j+1];
        }
        l[i][angle[i][0]] = angle[i][angle[i].size() - 1];
        r[i][angle[i][angle[i].size() - 1]] = angle[i][0];
        for(int j = 0;j < angle[i].size();j++){
            sc[i][angle[i][j]] += calc_ang(i,angle[i][j]);
        }
   }
   for(int i = 1;i <= n;i++) {
        tmp[i] = cal(i);
        se.insert(node(tmp[i],ele[i][3]));
   }
   while(se.size() != 0){
        auto it = se.begin();
        int x = seq[it->id];
        printf("%d\n",ele[x][3]);
        se.erase(it);
        for(int i = 0;i < 3;i++) res[i][ele[x][i]].erase(x);
        update(x);
   }
   return 0;
}

D: Distinctive Character

题意:

给出\(n(n <= 10 ^{5})\)长度为\(k\)的01串
求出一个串使得与这\(n\)个串相似度的最大值最小
相似度有多少位相同位相同

思路:

可以转化为成差异度的最小值最大。
将这个\(n\)个串丢到队列中bfs,每次枚举不同的一位生成新的串,这样就知道了从\(n\)个串到该新串差异度的最小值,找出最大的那个串输出即可
复杂度\(O(20 * 2 ^ {20})\)

#include<bits/stdc++.h>
#define P pair<int,int>
#define LL long long

using namespace std;

const int N = (1<<20);
int dis[N];
queue<int> q;
int main(){

   int n,k,s;
   cin>>n>>k;
   for(int i = 0;i < (1<<k);i++) dis[i] = -1;
   for(int i = 0;i < n;i++){
    string ss;
    s = 0;
    cin>>ss;
    for(int j = 0;j  < k;j++){
        if(ss[j] == '1') s += (1<<j);
    }
    q.push(s);
    dis[s] = 0;
   }
   int ans = 0,mx = 0;
   while(!q.empty()){
    int u = q.front();q.pop();
    for(int i = 0;i < k;i++){
        int v = u ^ (1<<i);
        if(dis[v] != -1) continue;
        dis[v] = dis[u] + 1;
        q.push(v);
        if(mx < dis[v]){
            mx = dis[v];
            ans = v;
        }
    }
   }
   for(int i = 0;i < k;i++) printf("%d", (ans & (1<<i)?1:0));
   printf("\n");
   return 0;
}

K: Kayaking Trip

思路:二分一个答案\(v\),那么对于所有的\(\frac{v}{c_i}\)
对于所有的\(s_n,s_b,s_e\)
能够找到\(\frac{n+b+e}{2}\)的对数
每一对对应满足\(s_{i1}+s_{i2}\) >= \(\frac{v}{c_i}\)
贪心的去选来判断是否满足即可
复杂度\(O(n log n)\)

#include<bits/stdc++.h>
#define P pair<int,int>
#define LL long long

using namespace std;

const int N = 1e5 + 10;
int a[3],b[3],n;
int c[N];
bool is(int mid){
    int x = a[0],y = a[1],z = a[2];
    for(int i = n / 2 - 1;i >= 0;i--){
        int p = mid / c[i] + (mid % c[i]?1:0);
        if(x >= 2 && 2* b[0] >= p) {
            x -= 2;
        }
        else if(x && y && b[0] + b[1] >= p){
            x--,y--;
        }else if(y >= 2 && 2 * b[1] >= p){
            if(x && z && b[0] + b[2] >= p && 2 * b[1] > b[0] + b[2]){
                x--,z--;
            }else {
                y -= 2;
            }
        }else if(x && z && b[0] + b[2] >= p){
          x--,z--;
        }else if(y && z && b[1] + b[2] >= p){
                y--,z--;
        }else if(z >= 2 && 2 * b[2] >= p){
            z -= 2;
        }
        else return false;
    }
    return true;
}
int main(){

   for(int i = 0;i < 3;i++) cin>>a[i];
   for(int i = 0;i < 3;i++) cin>>b[i];
   n = a[0] + a[1] + a[2];
   for(int i = 0;i < n / 2;i++) cin>>c[i];
   sort(c,c + n / 2);
   int L = 0,R = 100000 * 2000,ans = 0;
   while(L <= R){
    int mid = L + R >> 1;
    if(is(mid)) ans = max(ans,mid),L = mid + 1;
    else R = mid - 1;
   }
   cout<<ans<<endl;
   return 0;
}
posted @ 2017-11-01 22:32  jiachinzhao  阅读(720)  评论(0编辑  收藏  举报