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
",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("
");
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;
}