2017-2018 ACM-ICPC Northern Eurasia (Northeastern European Regional) Contest (NEERC 17)
A.Archery Tournament
假设当前询问为\(x,y\),那么能包含它的圆必然是其左边第一个直径\(\ge y\)的或者其右边第一个直径\(\ge y\)的,可以离线下来用线段树来找这两个圆,再判断一下点在不在圆内,先离散化所有点的横坐标,每个圆再线段树中的位置就以其离散化之后的\(x\)为基准
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
using LL = int_fast64_t;
const int MAXN = 2e5+7;
int n;
vector<int> vec;
struct SegmentTree{
int maxx[MAXN<<2], l[MAXN<<2], r[MAXN<<2], ID[MAXN<<2];
#define ls(rt) rt << 1
#define rs(rt) rt << 1 | 1
void pushup(int rt){ maxx[rt] = max(maxx[ls(rt)], maxx[rs(rt)]); }
void build(int L, int R, int rt){
l[rt] = L, r[rt] = R;
maxx[rt] = ID[rt] = 0;
if(L + 1 == R) return;
int mid = (L + R) >> 1;
build(L,mid,ls(rt)); build(mid,R,rs(rt));
}
void update(int pos, int rt, int id, int y){
if(l[rt] + 1 == r[rt]){
maxx[rt] = y;
ID[rt] = id;
return;
}
int mid = (l[rt] + r[rt]) >> 1;
if(pos<mid) update(pos,ls(rt),id,y);
else update(pos,rs(rt),id,y);
pushup(rt);
}
int QL(int x, int y, int rt){
if(l[rt]>x or maxx[rt]<y) return -1;
if(l[rt]+1==r[rt]) return ID[rt];
int ret = QL(x,y,rs(rt));
if(ret!=-1) return ret;
else return QL(x,y,ls(rt));
}
int QR(int x, int y, int rt){
if(r[rt]<=x or maxx[rt]<y) return -1;
if(l[rt]+1==r[rt]) return ID[rt];
int ret = QR(x,y,ls(rt));
if(ret!=-1) return ret;
else return QR(x,y,rs(rt));
}
}ST;
pair<int,pair<int,int>> Q[MAXN];
bool shoot(const pair<int,int> &sp, int p){
pair<int,int> O = Q[p].second;
if(1ll*O.second*O.second > (O.first-sp.first) * 1ll * (O.first-sp.first) + (O.second-sp.second) * 1ll *(O.second-sp.second)){
ST.update(lower_bound(vec.begin(),vec.end(),Q[p].second.first)-vec.begin()+1,1,0,0);
return true;
}
return false;
}
int main(){
____();
cin >> n;
ST.build(1,n+1,1);
for(int i = 1; i <= n; i++){
cin >> Q[i].first >> Q[i].second.first >> Q[i].second.second;
vec.emplace_back(Q[i].second.first);
}
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
for(int i = 1; i <= n; i++){
if(Q[i].first==1) ST.update(lower_bound(vec.begin(),vec.end(),Q[i].second.first)-vec.begin()+1,1,i,Q[i].second.second<<1);
else{
int nx = lower_bound(vec.begin(),vec.end(),Q[i].second.first) - vec.begin() + 1;
int p = ST.QL(nx,Q[i].second.second,1);
bool ok = false;
if(p!=-1) ok = shoot(Q[i].second,p);
if(!ok){
p = ST.QR(nx,Q[i].second.second,1);
if(p!=-1) ok = shoot(Q[i].second,p);
}
if(ok) cout << p << endl;
else cout << -1 << endl;
}
}
return 0;
}
B.Box
枚举判断即可
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
int a,b,c,w,h;
bool check(int n, int m){
if((a+b)*2<=n and (c+2*a<=m or c+2*b<=m)) return true;
if((a+c)*2<=n and (b+2*a<=m or b+2*c<=m)) return true;
if((b+c)*2<=n and (a+2*b<=m or a+2*c<=m)) return true;
if(3*a+b+c<=n and b+c <= m) return true;
if(3*b+a+c<=n and a+c <= m) return true;
if(3*c+a+b<=n and a+b <= m) return true;
return false;
}
int main(){
____();
cin >> a >> b >> c >> w >> h;
if(check(w,h) or check(h,w)) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
C.Connections
给出一张强连通图,要求保留\(2n\)条边,剩下的图仍然强连通
对\(1\)点跑一遍图,然后边取反,再对\(1\)点跑一遍图,经过的边即为剩下的,且最多有\(2n-2\)条,补到\(2n\)条即可
跑完之后选的边使得\(1\)号点可以到达任何点,同时任何点可以到达\(1\)号点,所以保证强连通
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e5+7;
vector<pair<int,int> > G[MAXN], rG[MAXN];
int n,m;
pair<int,int> edge[MAXN];
bool vis[MAXN],used[MAXN];
void dfs(int u, bool rev){
vis[u] = true;
for(auto e : (rev?rG[u]:G[u])){
if(!vis[e.first]){
used[e.second] = true;
dfs(e.first,rev);
}
}
}
void solve(){
cin >> n >> m;
for(int i = 1; i <= n; i++){
G[i].clear();
rG[i].clear();
}
for(int i = 1; i <= m; i++){
cin >> edge[i].first >> edge[i].second;
int u = edge[i].first, v = edge[i].second;
G[u].emplace_back(make_pair(v,i));
rG[v].emplace_back(make_pair(u,i));
used[i] = false;
}
for(int i = 1; i <= n; i++) vis[i] = false;
dfs(1,false);
for(int i = 1; i <= n; i++) vis[i] = false;
dfs(1,true);
int cnt = 0;
for(int i = 1; i <= m; i++){
if(!used[i]){
cnt++;
cout << edge[i].first << ' ' << edge[i].second << endl;
}
if(cnt==m-2*n) break;
}
}
int main(){
____();
int T;
for(cin >> T; T; T--) solve();
return 0;
}
D.Designing the Toy
给定三视图的面积,要求构造出一个立体图形满足条件,允许方块不叠在一起。
可以发现如果小的两个的乘积比大的还小是必然构造不了的,否则必然能够构造出一个某一个维度宽度为1的立体图形。
具体构造看代码,注意坐标的转换
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 111;
int a[3],b[3],match[3];
bool done[MAXN][MAXN];
int main(){
cin >> a[0] >> a[1] >> a[2];
b[0] = a[0]; b[1] = a[1]; b[2] = a[2];
sort(b,b+3);
if(b[0]*b[1]<b[2]){
cout << -1 << endl;
return 0;
}
cout << b[2] << endl;
for(int i = 1; i <= b[0]; i++) done[i][i] = true;
for(int i = b[0] + 1; i <= b[1]; i++) done[1][i] = true;
int delta = b[2] - b[1];
for(int i = 1; i <= b[0]; i++) for(int j = 1; j <= b[1]; j++){
if(!delta) break;
if(done[i][j]) continue;
delta--; done[i][j] = true;
}
for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++){
if(a[i]==b[j]){
b[j] = -1, match[i] = j;
break;
}
}
for(int i = 0; i < MAXN; i++) for(int j = 0; j < MAXN; j++){
if(!done[i][j]) continue;
if(match[0]==2){
if(match[1]==0) cout << i << ' ' << j << ' ' << 1 << endl;
else cout << j << ' ' << i << ' ' << 1 << endl;
}
else if(match[1]==2){
if(match[0]==0) cout << i << ' ' << 1 << ' ' << j << endl;
else cout << j << ' ' << 1 << ' ' << i << endl;
}
else{
if(match[0]==0) cout << 1 << ' ' << i << ' ' << j << endl;
else cout << 1 << ' ' << j << ' ' << i << endl;
}
}
return 0;
}
E.Easy Quest
简单签到
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1111;
int n,A[MAXN];
vector<int> vec;
int main(){
____();
cin >> n;
for(int i = 1; i <= n; i++){
int x; cin >> x;
if(x>=0) A[x]++;
else{
if(A[-x]) A[-x]--;
else{
if(A[0]) A[0]--, vec.emplace_back(-x);
else{
cout << "No" << endl;
return 0;
}
}
}
}
while(A[0]) A[0]--, vec.emplace_back(1);
cout << "Yes" << endl;
for(int x : vec) cout << x << ' '; cout << endl;
return 0;
}
F.The Final Level
分四种情况枚举判断一下,要记录一下上一次放的最后一个位置和当前位置的相对位置,先把坐标转换成正的,最后在转换回去
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
int a,b,n,fa,fb;
vector<pair<pair<int,int>,pair<int,int>>> ret;
ostream& operator << (ostream &os, const pair<pair<int,int>,pair<int,int>> pr){
os << pr.first.first*fa << ' ' << pr.first.second*fb << ' ' << pr.second.first*fa << ' ' << pr.second.second*fb;
return os;
}
void dfs(int ca, int cb, int vert){
int dx = a - ca, dy = b - cb;
if(dx<n and dy < n){
if(vert) ret.emplace_back(make_pair(make_pair(ca+dx,cb+n-1),make_pair(ca-(n-1-dx),cb)));
else ret.emplace_back(make_pair(make_pair(ca,cb-(n-1-dy)),make_pair(ca+n-1,cb+dy)));
return;
}
else if(dx<n){
ret.emplace_back(make_pair(make_pair(ca,cb),make_pair(ca+n-1,cb+n-1)));
dfs(a,cb+n,1);
}
else if(dy<n){
ret.emplace_back(make_pair(make_pair(ca+n-1,cb+n-1),make_pair(ca,cb)));
dfs(ca+n,b,0);
}
else{
ret.emplace_back(make_pair(make_pair(ca,cb),make_pair(ca+n-1,cb+n-1)));
if(dx<dy) dfs(ca+n-1,cb+n,1);
else dfs(ca+n,cb+n-1,0);
}
}
void solve(){
cin >> a >> b >> n;
a = a * (fa = a < 0 ? -1 : 1);
b = b * (fb = b < 0 ? -1 : 1);
ret.clear();
dfs(0,0,1);
cout << ret.size() << endl;
for(auto p : ret) cout << p << endl;
}
int main(){
____();
int T; for(cin >> T; T; T--) solve();
return 0;
}
G.The Great Wall
H.Hack
I.Interactive Sort
J.Journey from Petersburg to Moscow
给出一张图,求\(1\)到\(n\)的最短路,其中如果经过的路径超过\(k\)条,只选择前\(k\)条的花费
枚举第\(k+1\)条边的长度\(x\),建图时边的长度变成\(max(0,w-x)\),最后答案为\(dist[n]+k\cdot x\),找最小值即可
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
using LL = int_fast64_t;
const int MAXN = 3333;
int n,m,k;
vector<pair<int,int>> G[MAXN];
LL dist[MAXN];
LL Dijkstra(int x){
memset(dist,0x3f,sizeof(dist));
dist[1] = 0;
priority_queue<pair<LL,int>,vector<pair<LL,int>>,greater<pair<LL,int>>> que;
que.push(make_pair(dist[1],1));
while(!que.empty()){
auto p = que.top();
que.pop();
int u = p.second;
LL d = p.first;
if(dist[u]!=d) continue;
for(auto e : G[u]){
int v = e.first, w = max(e.second-x,0);
if(dist[u]+w<dist[v]){
dist[v] = dist[u] + w;
que.push(make_pair(dist[v],v));
}
}
}
return dist[n] + 1ll * k * x;
}
int main(){
scanf("%d %d %d",&n,&m,&k);
vector<int> vec;
for(int i = 1; i <= m; i++){
int u, v, w;
scanf("%d %d %d",&u,&v,&w);
G[u].emplace_back(make_pair(v,w));
G[v].emplace_back(make_pair(u,w));
vec.emplace_back(w);
}
vec.emplace_back(0);
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
LL ret = INT64_MAX;
for(int x : vec) ret = min(ret,Dijkstra(x));
printf("%I64d\n",ret);
return 0;
}
K.Knapsack Cryptosystem
L.Laminar Family
给定一棵树和其中的一些链,问是否任何两条链只存在包含或者交集为\(0\)的情况
树链剖分,以\(lca\)深度为第一关键字,链长为第二关键字排序所有的链,这样能保证之后的链不可能包含之前的链,只要考虑之后的链是否和之前的链相交即可
然后找链上最小值和最大值,判断是否相等
每次找完之后链上各点权值+1
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e5+7;
const int INF = 0x3f3f3f3f;
vector<int> G[MAXN];
int n,m,dfn[MAXN],rdfn[MAXN],depth[MAXN],son[MAXN],top[MAXN],sz[MAXN],par[MAXN][20],idx;
struct SegmentTree{
int minn[MAXN<<2],maxx[MAXN<<2],l[MAXN<<2],r[MAXN<<2],lazy[MAXN<<2];
#define ls(rt) rt << 1
#define rs(rt) rt << 1 | 1
void pushup(int rt){
minn[rt] = min(minn[ls(rt)],minn[rs(rt)]);
maxx[rt] = max(maxx[ls(rt)],maxx[rs(rt)]);
}
void build(int L, int R, int rt = 1){
l[rt] = L; r[rt] = R;
if(L+1==R) return;
int mid = (L+R) >> 1;
build(L,mid,ls(rt)); build(mid,R,rs(rt));
}
void pushdown(int rt){
if(!lazy[rt]) return;
lazy[ls(rt)] += lazy[rt]; lazy[rs(rt)] += lazy[rt];
minn[ls(rt)] += lazy[rt]; minn[rs(rt)] += lazy[rt];
maxx[ls(rt)] += lazy[rt]; maxx[rs(rt)] += lazy[rt];
lazy[rt] = 0;
}
int QMIN(int L, int R, int rt = 1){
if(l[rt]>=R or L>=r[rt]) return INF;
if(L<=l[rt] and r[rt]<=R) return minn[rt];
pushdown(rt);
return min(QMIN(L,R,ls(rt)),QMIN(L,R,rs(rt)));
}
int QMAX(int L, int R, int rt = 1){
if(l[rt]>=R or L>=r[rt]) return 0;
if(L<=l[rt] and r[rt]<=R) return maxx[rt];
pushdown(rt);
return max(QMAX(L,R,ls(rt)),QMAX(L,R,rs(rt)));
}
void update(int L, int R, int rt = 1){
if(l[rt]>=R or L>=r[rt]) return;
if(L<=l[rt] and r[rt]<=R){
lazy[rt]++; maxx[rt]++; minn[rt]++;
return;
}
pushdown(rt);
update(L,R,ls(rt)); update(L,R,rs(rt));
pushup(rt);
}
}ST;
void modify(int u, int v){
while(top[u]!=top[v]){
if(depth[top[u]]<depth[top[v]]) swap(u,v);
ST.update(dfn[top[u]],dfn[u]+1);
u = par[top[u]][0];
}
if(depth[u]<depth[v]) swap(u,v);
ST.update(dfn[v],dfn[u]+1);
}
int qmin(int u, int v){
int minn = INF;
while(top[u]!=top[v]){
if(depth[top[u]]<depth[top[v]]) swap(u,v);
minn = min(minn,ST.QMIN(dfn[top[u]],dfn[u]+1));
u = par[top[u]][0];
}
if(depth[u]<depth[v]) swap(u,v);
return min(minn,ST.QMIN(dfn[v],dfn[u]+1));
}
int qmax(int u, int v){
int maxx = 0;
while(top[u]!=top[v]){
if(depth[top[u]]<depth[top[v]]) swap(u,v);
maxx = max(maxx,ST.QMAX(dfn[top[u]],dfn[u]+1));
u = par[top[u]][0];
}
if(depth[u]<depth[v]) swap(u,v);
return max(maxx,ST.QMAX(dfn[v],dfn[u]+1));
}
bool check(int u, int v){ return qmax(u,v)==qmin(u,v); }
void dfs1(int u, int f){
sz[u] = 1; depth[u] = depth[par[u][0] = f] + 1;
for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
for(int v : G[u]){
if(v==f) continue;
dfs1(v,u); sz[u] += sz[v];
if(sz[v]>sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int tp){
dfn[u] = ++idx; rdfn[idx] = u;
top[u] = tp;
if(son[u]) dfs2(son[u],tp);
for(int v : G[u]){
if(v==par[u][0] or v==son[u]) continue;
dfs2(v,v);
}
}
int LCA(int u, int v){
if(depth[u]<depth[v]) swap(u,v);
for(int i = 0; i < 20; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
if(u==v) return u;
for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
u = par[u][i];
v = par[v][i];
}
return par[u][0];
}
int main(){
____();
cin >> n >> m;
for(int i = 1; i < n; i++){
int u, v; cin >> u >> v;
G[u].emplace_back(v);
G[v].emplace_back(u);
}
dfs1(1,0); dfs2(1,1);
vector<pair<pair<int,int>,pair<int,int>>> vec(m);
for(int i = 0; i < m; i++){
cin >> vec[i].second.first >> vec[i].second.second;
int lca = LCA(vec[i].second.first,vec[i].second.second);
vec[i].first.first = lca;
vec[i].first.second = depth[vec[i].second.first] + depth[vec[i].second.second] - 2 * depth[lca];
}
sort(vec.begin(),vec.end(),[](const pair<pair<int,int>,pair<int,int>> &lhs, const pair<pair<int,int>,pair<int,int>> &rhs){
if(depth[lhs.first.first]==depth[rhs.first.first]) return lhs.first.second > rhs.first.second;
else return depth[lhs.first.first] < depth[rhs.first.first];
});
ST.build(1,MAXN);
for(auto pr : vec){
if(!check(pr.second.first,pr.second.second)){
cout << "No" << endl;
return 0;
}
modify(pr.second.first,pr.second.second);
}
cout << "Yes" << endl;
return 0;
}