Nordic Collegiate Programming Contest (NCPC 2021)(SMU 2024 ICPC网络赛选拔赛)
Nordic Collegiate Programming Contest (NCPC 2021)(SMU 2024 ICPC网络赛选拔赛)
A Antenna Analysis
思路
原式可拆成:
\[(x_i-x_j)-c(i-j)=(x_i-c\cdot i)+(-x_j+c\cdot j)
\]
分别维护下两个的最大值即可。
代码
#include<bits/stdc++.h>
#define int long long
#define ll __int128
const int mod= 998244353;
#define PII pair<int,int>
#define PIII pair<int,PII>
#define INF 0x3f3f3f3f
#define double long double
using ull = std::uint64_t;
#define endl '\n'
#pragma GCC optimize(2)
using namespace std;
priority_queue<int,vector<int>,less<int>>q1;
priority_queue<int,vector<int>,less<int>>q2;
void solve() {
int n,c;
cin>>n>>c;
for(int i=1;i<=n;i++){
int x;
cin>>x;
q1.push(c*i-x);
q2.push(c*i+x);
if(i>1){
cout<<max(q1.top()+x-c*i,q2.top()-x-c*i)<<" \n"[i==n];
}else{
cout<<0<<" \n"[i==n];
}
}
}
int32_t main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
C Customs Controls
思路
隔得久有点忘了,大致思路就是,从起点和它所连的边都染同一个色,这样就可以封死这些路,假设有 \(x\) 条出路只能染 \(y\) 条,那么剩下的 \(x-y\) 条我们可以找到终点对应的这 \(x-y\) 条,然后把它们和终点又染同一个色,这样就可以把剩下的 \(x-y\) 条也封死。
建议画图手玩一下,只有 \(n=2,k=1\) 这种情况是 impossible
。
代码
#include<bits/stdc++.h>
#define ll long long
#define int long long
const int mod= 998244353;
#define PII pair<ll,int>
#define PIII pair<int,PII>
#define endl '\n'
#pragma GCC optimize(2)
using namespace std;
const int N=1e6+3;
vector<int>g[N];
int dist[N];
int a[N];
void solve() {
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= n; ++i) {
dist[i] = LLONG_MAX / 2;
}
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
for (int i = 0; i < m; ++i) {
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
if (n == 2 and k == 1) {
cout << "impossible\n";
return;
}
dist[1] = a[1];
priority_queue<PII, vector<PII >, greater<PII>> q;
vector<int> vis(n + 1);
q.push({dist[1], 1});
while (q.size()) {
auto [w, u] = q.top();
q.pop();
if (vis[u])continue;
vis[u] = 1;
for (auto v: g[u]) {
if (dist[v] > w + a[v]) {
dist[v] = w + a[v];
q.push({dist[v], v});
}
}
}
vector<int> f[n + 1], tu[n + 1];
fill(vis.begin(), vis.end(), 0);
queue<int> p;
p.push(n);
while (p.size()) {
auto dq = p.front();
p.pop();
vis[dq] = 1;
for (auto v: g[dq]) {
if (dist[v] + a[dq] == dist[dq]) {
f[dq].push_back(v);
f[v].push_back(dq);
tu[v].push_back(dq);
if (vis[v])continue;
vis[v] = 1;
p.push(v);
}
}
}
fill(vis.begin(), vis.end(), 0);
vector<char> ans(n + 1, '.');
int x, y;
auto puts = [&]() {
for (int i = 1; i <= n; ++i) {
if (ans[i] == '.') {
if (x) {
x--;
ans[i] = 'N';
} else {
y--;
ans[i] = 'S';
}
}
cout << ans[i];
}
};
int cnt = 0;
for (int i = 1; i <= n; ++i) {
if (f[i].size()) {
cnt++;
}
}
x = k, y = n - k;
if (k > n - k) {
x = k, y = n - k;
////x为N,Y为S
///X大
int ax = f[1].size();
int bx = f[n].size();
if (ax + 1 <= x) {
ans[1] = 'N';
vis[1] = 1;
queue<int> q;
for (auto v: f[1]) {
ans[v] = 'N';
vis[v] = 1;
q.push(v);
}
x -= ax + 1;
puts();
return;
}
if (bx + 1 <= x) {
ans[n] = 'N';
vis[n] = 1;
queue<int> q;
for (auto v: f[n]) {
ans[v] = 'N';
vis[v] = 1;
q.push(v);
}
x -= bx + 1;
puts();
return;
}
} else {
x = k, y = n - k;
////x为N,Y为S
///Y大
int ax = f[1].size();
int bx = f[n].size();
if (ax + 1 <= y) {
ans[1] = 'S';
vis[1] = 1;
queue<int> q;
for (auto v: f[1]) {
ans[v] = 'S';
vis[v] = 1;
q.push(v);
}
y -= ax + 1;
puts();
return;
}
if (bx + 1 <= y) {
ans[n] = 'S';
vis[n] = 1;
queue<int> q;
for (auto v: f[n]) {
ans[v] = 'S';
vis[v] = 1;
q.push(v);
}
y -= bx + 1;
puts();
return;
}
}
if (x) {
ans[1] = 'N';
x--;
}
for (auto v: f[1]) {
if (x) {
ans[v] = 'N';
x--;
} else {
ans[v] = 'S';
y--;
}
}
puts();
return;
}
int32_t main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
D Deceptive Directions
思路
因为给出的是最短到达宝藏的指令,所以宝藏一定是距离起点 S
长度为 \(s.size()\) 的地方。
又因为每一步都是错误指令,那么每一步都有其他三个方向的可能,按照每步三个方向去 BFS,最后判断到达的距离是否和指令长度相等即可。
细节部分需要好好处理,比如 BFS 时超过了指令的长度等。
代码
#include<bits/stdc++.h>
#define ll long long
#define int long long
const int mod= 998244353;
#define PII pair<ll,int>
#define PIII pair<int,PII>
#define endl '\n'
#pragma GCC optimize(2)
using namespace std;
const int N=1e6+3;
const int maxn=1009;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
void solve() {
int n, m;
cin >> m >> n;
string s;
vector<vector<char>> g(n, vector<char>(m));
vector<vector<int>> dist(n, vector<int>(m, INT_MAX));
vector<vector<int>> dist2(n, vector<int>(m, INT_MAX));
vector<vector<int>> vis(n, vector<int>(m, 0));
queue<PII > q;
int stx, sty;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> g[i][j];
if (g[i][j] == 'S') {
stx = i, sty = j;
}
}
}
cin>>s;
dist[stx][sty] = 0;
q.push({stx, sty});
while (q.size()) {
auto [x, y] = q.front();
q.pop();
for (int i = 0; i < 4; ++i) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 0 or nx >= n or ny < 0 or ny >= m)continue;
if (g[nx][ny] == '#')continue;
if (dist[nx][ny] > dist[x][y] + 1) {
dist[nx][ny] = dist[x][y] + 1;
q.push({nx, ny});
}
}
}
dist2[stx][sty]=0;
q.push({stx,sty});
while(q.size()){
auto [x,y]=q.front();
q.pop();
vis[x][y]=1;
int d=dist2[x][y];
if(d>=s.size())continue;
if(s[d]=='N'){
d=0;
}
else if(s[d]=='S'){
d=1;
}
else if(s[d]=='W'){
d=2;
}
else {
d=3;
}
for (int i = 0; i <4 ; ++i) {
if(i==d)continue;
int nx=x+dx[i];
int ny=y+dy[i];
if (nx < 0 or nx >= n or ny < 0 or ny >= m)continue;
if (g[nx][ny] == '#')continue;
if(vis[nx][ny])continue;
if(dist2[x][y]+1==dist[nx][ny]){
dist2[nx][ny]=dist[nx][ny];
vis[nx][ny]=1;
q.push({nx,ny});
}
}
}
for (int i = 0; i <n ; ++i) {
for (int j = 0; j <m ; ++j) {
if(dist2[i][j]==s.size()){
g[i][j]='!';
}
}
}
for (int i = 0; i <n ; ++i) {
for (int j = 0; j <m ; ++j) {
cout<<g[i][j];
}
cout<<endl;
}
}
int32_t main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
G Grazed Grains
思路
求圆面积并。
队友写的圆面积交,然后求 \(1\sim n\) 的圆的交的和。
也可以用自适应辛普森乱搞,比如洛谷这道圆面积并模板题CIRU - The area of the union of circles - 洛谷),直接把AC代码放在这题上即可通过。
(圆面积交求圆面积并)代码
#include<bits/stdc++.h>
#define ll long long
#define int long long
const int mod= 998244353;
#define PII pair<ll,int>
#define PIII pair<int,PII>
#define endl '\n'
#pragma GCC optimize(2)
using namespace std;
const int N=1e6+3;
const int maxn=1009;
const double eps=1e-8;
const double pi= acos(-1);
int dcmp(double x){
return fabs(x)<eps?0:(x<0?-1:1);
}
struct circle{
double x,y,r,angle;
int d;
circle(){}
circle(double x,double y,double angle=0,int d=0)
:x(x),y(y),angle(angle),d(d) {}
};
typedef circle Circle;
circle cir[maxn],tp[maxn<<1];
double area[maxn];
double sqr(double x){
return x*x;
}
double dis(circle a,circle b){
return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
double cross(circle a,circle b,circle c){
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
int CirCrossCir(Circle p1,double r1,Circle p2 ,double r2,Circle &cp1,Circle&cp2) {
double mx = p2.x - p1.x, sx = p2.x + p1.x, mx2 = mx * mx;
double my = p2.y - p1.y, sy = p2.y + p1.y, my2 = my * my;
double sq = mx2 + my2, d = -(sq - sqr(r1 - r2)) * (sq - sqr(r1 + r2));
if(d+eps<0 )return 0;
if(d<eps)d=0;
else d=sqrt(d);
double x=mx*((r1+r2)*(r1-r2)+mx*sx)+sx*my2;
double y=my*((r1+r2)*(r1-r2)+my*sy)+sy*mx2;
double dx=mx*d,dy=my*d;
sq*=2;
cp1.x=(x-dy)/sq;
cp1.y=(y+dx)/sq;
cp2.x=(x+dy)/sq;
cp2.y=(y-dx)/sq;
if(d>eps)return 2;
else return 1;
}
bool circmp(const Circle& u,const Circle& v){
return dcmp(u.r-v.r)<0;
}
bool cmp(const Circle& u,const Circle& v){
if(dcmp(u.angle-v.angle)){
return u.angle<v.angle;
}
return u.d>v.d;
}
double calc(Circle cir,Circle cp1,Circle cp2){
double ans=(cp2.angle-cp1.angle)*sqr(cir.r)
- cross(cir,cp1,cp2)+ cross(Circle(0,0),cp1,cp2);
return ans/2;
}
void CirUnion(Circle cir[],int n){
Circle cp1,cp2;
sort(cir,cir+n,circmp);
for (int i = 0; i <n ; ++i) {
for (int j = i+1; j <n ; ++j) {
if(dcmp(dis(cir[i],cir[j])+cir[i].r-cir[j].r)<=0){
cir[i].d++;
}
}
}
for (int i = 0; i <n ; ++i) {
int tn=0,cnt=0;
for (int j = 0; j <n ; ++j) {
if(i==j)continue;
if(CirCrossCir(cir[i],cir[i].r,cir[j],cir[j].r,cp2,cp1)<2)continue;
cp1.angle=atan2(cp1.y-cir[i].y,cp1.x-cir[i].x);
cp2.angle=atan2(cp2.y-cir[i].y,cp2.x-cir[i].x);
cp1.d=1;
tp[tn++]=cp1;
cp2.d=-1;
tp[tn++]=cp2;
if(dcmp(cp1.angle-cp2.angle)>0){
cnt++;
}
}
tp[tn++]=Circle (cir[i].x-cir[i].r,cir[i].y,pi,-cnt);
tp[tn++]=Circle (cir[i].x-cir[i].r,cir[i].y,-pi,cnt);
sort(tp,tp+tn,cmp);
int p,s=cir[i].d+tp[0].d;
for (int j = 1; j <tn ; ++j) {
p=s;
s+=tp[j].d;
area[p]+= calc(cir[i],tp[j-1],tp[j]);
}
}
}
int n;
void solve() {
::scanf("%lld",&n);
for (int i = 0; i <n ; ++i) {
::scanf("%lf%lf%lf",&cir[i].x,&cir[i].y,&cir[i].r);
// ::printf("%f %f %f",cir[i].x,cir[i].y,cir[i].r);
cir[i].d=1;
}
::memset(area,0,sizeof area);
CirUnion(cir,n);
double ans=0;
for (int i = 1; i <=n ; ++i) {
area[i]-=area[i+1];
ans+=area[i];
}
::printf("%.10lf\n",ans);
}
int32_t main() {
// std::ios::sync_with_stdio(false);
// cin.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
(自适应辛普森)代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const double eps = 1e-8; // 根据题目精度要求进行修改
const double PI = acos(-1.0); // pai, 3.1415916....
const int N = 2024;
struct seg {
double l, r;
friend bool operator <(seg a, seg b) {
if (a.l == b.l) return a.r < b.r;
return a.l < b.l;
}
};
struct vec {
i64 x, y;
};
double dis(vec a, vec b) {
return sqrt(1.0 * (a.x - b.x) * (a.x - b.x) + 1.0 * (a.y - b.y) * (a.y - b.y));
}
struct cir {
vec o;
i64 r;
friend bool operator <(cir a, cir b) {
return a.r < b.r;
}
} a[N];
int n;
vector<seg> v;
int st, ed;
double f(double x) { //求f(x)
v.clear();
for (int i = st; i <= ed; ++i) { //处理出交点的纵坐标,保存为线段
if (x > a[i].o.x + a[i].r || x < a[i].o.x - a[i].r) continue;
double l = x - a[i].o.x;
l = sqrt(a[i].r * a[i].r - l * l);
v.push_back((seg) {a[i].o.y - l, a[i].o.y + l});
}
if (v.empty()) return 0.0;
sort(v.begin(), v.end()); //以下贪心线段覆盖
double ans = 0, last = v[0].l;
for (int i = 0; i < v.size(); ++i) {
if (v[i].r > last) {
ans += v[i].r - max(last, v[i].l);
last = v[i].r;
}
}
return ans;
}
double calc(double l, double r, double sl, double sr) { //直接计算一段区间的面积
double mid = (l + r) / 2;
return (r - l) * (sl + 4.0 * f(mid) + sr) / 6.0;
}
double simpson(double l, double r, double S, double sl, double sr) {
double mid = (l + r) / 2, s = f(mid);
double t1 = calc(l, mid, sl, s), t2 = calc(mid, r, s, sr);
if (fabs(t1 + t2 - S) < eps) return S;
return simpson(l, mid, t1, sl, s) + simpson(mid, r, t2, s, sr);
}
void init() { //预处理,删除被包含的圆
int cnt = 0;
for (int i = 1, j; i <= n; ++i) {
for (j = i + 1; j <= n; ++j) {
if (a[i].r - a[j].r + dis(a[i].o, a[j].o) < eps) {
break;
}
}
if (j > n) a[++cnt] = a[i];
}
n = cnt;
}
bool cmp(cir a, cir b) { //按圆的最左端排序,用于判断连续相交
return a.o.x - a.r < b.o.x - b.r;
}
double ans;
void solve() { //寻找每段联通的圆,进行计算
double l, r;
int i = 1, j;
while (i <= n) {
l = a[i].o.x - a[i].r, r = a[i].o.x + a[i].r;
for (j = i + 1; j <= n; ++j) {
if (a[j].o.x - a[j].r > r) break;
r = max(r, (double)a[j].o.x + a[j].r);
}
st = i, ed = j - 1, i = j;
double mid = (l + r) / 2;
ans += simpson(l, r, f(mid), f(l), f(r));
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i ++) {
int x, y, r;
cin >> x >> y >> r;
a[i] = {x, y, r};
}
sort(a + 1, a + n + 1);
init();
sort(a + 1, a + n + 1, cmp);
solve();
cout << fixed << setprecision(3) << ans << '\n';
return 0;
}
J Joint Jog Jam
思路
直接计算,队友写的,已经眼花缭乱了
代码
#include<bits/stdc++.h>
#define int long long
#define ll __int128
const int mod= 998244353;
#define PII pair<int,int>
#define PIII pair<int,PII>
#define INF 0x3f3f3f3f
#define double long double
using ull = std::uint64_t;
#define endl '\n'
#pragma GCC optimize(2)
using namespace std;
void solve() {
int x[5],y[5];
for(int i=1;i<=4;i++){
cin>>x[i]>>y[i];
}
int a,b,c;
c=(x[1]-x[2])*(x[1]-x[2])+(y[1]-y[2])*(y[1]-y[2]);
b=2*((x[1]-x[2])*(x[2]+x[3]-x[1]-x[4])+(y[1]-y[2])*(y[2]+y[3]-y[1]-y[4]));
a=(x[2]+x[3]-x[1]-x[4])*(x[2]+x[3]-x[1]-x[4])+(y[2]+y[3]-y[1]-y[4])*(y[2]+y[3]-y[1]-y[4]);
double mx=0;
if(a!=0)mx=(double)(4*a*c-b*b)/4.0/a;
mx=max(mx,1.0L*(x[1]-x[2])*(x[1]-x[2])+(y[1]-y[2])*(y[1]-y[2]));
mx=max(mx,1.0L*(x[3]-x[4])*(x[3]-x[4])+(y[3]-y[4])*(y[3]-y[4]));
cout<< fixed << setprecision(10)<<sqrtl(mx)<<endl;
}
int32_t main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
K Knot Knowledge
思路
排序后比较第一个不同的即可,否则输出最后一个。
代码
#include<bits/stdc++.h>
#define int long long
#define ll __int128
const int mod= 998244353;
#define PII pair<ll,int>
#define PIII pair<int,PII>
#define INF 0x3f3f3f3f
#define double long double
using ull = std::uint64_t;
#define endl '\n'
#pragma GCC optimize(2)
using namespace std;
vector<int>q1,q2;
void solve() {
int n;
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
q1.push_back(x);
}
for(int i=1;i<n;i++){
int x;
cin>>x;
q2.push_back(x);
}
sort(q1.begin(),q1.end());
sort(q2.begin(),q2.end());
for(int i=0;i<n-1;i++){
if(q1[i]!=q2[i]){
cout<<q1[i]<<endl;
return;
}
}
cout<<q1[n-1]<<endl;
}
int32_t main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
L Locust Locus
思路
找 \(c_1,c_2\) 的最小公倍数加上年份即可。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
int ans = INT_MAX / 2;
for (int i = 0; i < n; i ++) {
int y, c1, c2;
cin >> y >> c1 >> c2;
ans = min(ans, y + lcm(c1, c2));
}
cout << ans << '\n';
return 0;
}