牛客多校第六场题解
Contracting Convex Hull
https://ac.nowcoder.com/acm/contest/11257/A
A - Contracting Convex Hull
solved by Tryna.(-)
题意: 许多半平面正在匀速移动,每次询问给定一个时刻,求该时刻下半平面的交构成的凸包的面积
题解:
- 凸包收缩时每个顶点都是沿着角平分线移动的,当运动到相邻两个对角线相交的顶点,就会两点重合,凸包的形状就会发生变化
- 对于当前这个凸包,我们找到最近的那个使凸包发生变化的顶点,我们已知移动的距离,可以通过三角函数求出运动的时间,知道了时间就可以以同样的方法求出其他顶点运动的距离,新的凸包就是已知的,然后就重复操作
- 对于每次变化,凸包总有一段时间形状不变,然后我们就可以求出在这段时间之内的询问,面积是关于时间的一个二次函数,聚聚们tql,我根本推不出这个函数
- 其他也没啥了,注意点细节就能过了
#include<bits/stdc++.h>
using namespace std;
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err () { cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...);}
#define ll long long
#define ull unsigned long long
#define LL __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define PII pair<ll, ll>
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define em emplace
#define mp(a,b) make_pair(a,b)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define PAUSE system("pause");
const double Pi = acos(-1.0);
const double eps = 1e-6;
const int maxn = 1e3 + 10;
const int maxm = 1e5 + 10;
const int mod = 998244353;
inline ll rd() {
ll f = 0; ll x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
if (f) x = -x;
return x;
}
void out(ll a) {if(a<0)putchar('-'),a=-a;if(a>=10)out(a/10);putchar(a%10+'0');}
#define pt(x) out(x),puts("")
inline void swap(ll &a, ll &b){ll t = a; a = b; b = t;}
inline void swap(int &a, int &b){int t = a; a = b; b = t;}
inline ll min(ll a, ll b){return a < b ? a : b;}
inline ll max(ll a, ll b){return a > b ? a : b;}
ll qpow(ll n,ll k,ll mod) {ll ans=1;while(k){if(k&1)ans=ans*n%mod;n=n*n%mod;k>>=1;}return ans%mod;}
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
int sgn(double x){
if(fabs(x) < eps) return 0;
else return x < 0?-1:1;
}
struct Point{
double x,y;
Point(){}
Point(double x,double y):x(x),y(y){}
Point operator + (Point B){return Point(x + B.x,y + B.y);}
Point operator - (Point B){return Point(x - B.x,y - B.y);}
Point operator * (double k){return Point(x*k,y*k);}
Point operator / (double k){return Point(x/k,y/k);}
double operator ^ (Point B) {
return x * B.y - y * B.x;
}
bool operator == (Point B){return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;}
double distance(Point B) {
return sqrt((x - B.x) * (x - B.x) + (y - B.y) * (y - B.y));
}
}p[maxn], pos[maxn], pp[maxn];
struct Line{
Point p1,p2;
Line(){};
Line(Point p1,Point p2):p1(p1),p2(p2){}
}L[maxn];
Point Cross_point(Point a, Point b, Point c, Point d){
double s1 = (b - a)^(c - a);
double s2 = (b - a)^(d - a);
return Point(c.x * s2 - d.x * s1, c.y * s2 - d.y * s1) / (s2 - s1);
}
double getangle(Point p1, Point p2, Point p3) {
double theta = atan2(p1.x - p3.x, p1.y - p3.y) - atan2(p2.x - p3.x, p2.y - p3.y);
if(theta > Pi)
theta -= Pi * 2;
if (theta < -Pi)
theta += Pi * 2;
theta = abs(theta * 180.0 / Pi);
return theta;
}
struct query{
int id;
double sec;
bool operator < (const query &r) const {
return sec < r.sec;
}
}q[maxm];
int n, m;
double tot;
double angle[maxn];
double ans[maxm];
double A, B, C;
void solve(){
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%lf %lf", &p[i].x, &p[i].y);
}
scanf("%d", &m);
for(int i = 1; i <= m; i++) {
scanf("%lf", &q[i].sec);
q[i].id = i;
}
sort(q + 1, q + 1 + m);
int now = 1;
while(1) {
A = B = C = 0;
for(int i = 0; i < n; i++) { // 求角平分线 true
int nxt = (i + 1) % n;
int pre = (i - 1 + n) % n;
double d1 = p[i].distance(p[pre]);
double d2 = p[i].distance(p[nxt]);
Point tmp = p[i] + (p[nxt] - p[i]) * d1 / d2;
Point mid = (tmp + p[pre]) / 2;
L[i] = Line(p[i], mid);
}
for(int i = 0; i < n; i++) { // 求角平分线交点 true
pos[i] = Cross_point(L[i].p1, L[i].p2, L[(i + 1) % n].p1, L[(i + 1) % n].p2);
double d = p[i].distance(p[(i + 1) % n]);
A += (p[i] ^ p[(i + 1) % n]) / 2;
B += d;
C += d * d / (fabs((p[(i + 1) % n] - pos[i]) ^ (p[i] - pos[i]))) * 0.5;
}
double minn = INF;
for(int i = 0; i < n; i++) {
double d = p[i].distance(pos[i]);
int nxt = (i + 1) % n;
int pre = (i - 1 + n) % n;
angle[i] = getangle(p[nxt], p[pre], p[i]) / 2;
angle[i] = sin(angle[i] * Pi / 180);
double t = angle[i] * d;
minn = min(t, minn);
}
while(1) {
if(sgn(q[now].sec - tot - minn) > 0) break;
double x = q[now].sec - tot;
ans[q[now].id] = A - B * x + C * x * x;
now++;
if(now > m) break;
}
tot += minn;
int num = 0;
for(int i = 0; i < n; i++) {
double d1 = minn / angle[i];
double d2 = p[i].distance(pos[i]);
p[i] = p[i] + (pos[i] - p[i]) * (d1 / d2);
}
for(int i = 0; i < n; i++) {
if(p[i] == p[(i + 1) % n])
continue;
p[num++] = p[i];
}
n = num;
if(n < 3) {
for(int i = now; i <= m; i++) {
ans[q[i].id] = 0;
}
break;
}
}
for(int i = 1; i <= m; i++)
printf("%.10f\n", ans[i]);
}
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt", "w", stdout);
int t = 1;
// t = rd();
// scanf("%d", &t);
// cin >> t;
while(t--)
solve();
PAUSE;
return 0;
} C - Delete Edges
题意: 给出一张完全图,每次可以删掉一个三元环,使得最后边数, 输出方案数量以及方案
题解: 结论题,输出的所有解。
#include<bits/stdc++.h>
using namespace std;
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err () { cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...);}
#define ll long long
#define ull unsigned long long
#define LL __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define PII pair<ll, ll>
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define em emplace
#define mp(a,b) make_pair(a,b)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define PAUSE system("pause");
const double Pi = acos(-1.0);
const double eps = 1e-10;
const int maxn = 5e4 + 10;
const int maxm = 6e2 + 10;
const int mod = 998244353;
const int S = 5;
inline ll rd() {
ll f = 0; ll x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
if (f) x = -x;
return x;
}
void out(ll a) {if(a<0)putchar('-'),a=-a;if(a>=10)out(a/10);putchar(a%10+'0');}
#define pt(x) out(x),puts("")
inline void swap(ll &a, ll &b){ll t = a; a = b; b = t;}
inline void swap(int &a, int &b){int t = a; a = b; b = t;}
inline ll min(ll a, ll b){return a < b ? a : b;}
inline ll max(ll a, ll b){return a > b ? a : b;}
ll qpow(ll n,ll k,ll mod) {ll ans=1;while(k){if(k&1)ans=ans*n%mod;n=n*n%mod;k>>=1;}return ans%mod;}
int n;
void solve(){
scanf("%d", &n);
vector<pii> v;
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++) {
int k = (n - i - j + n) % n;
if(k == 0) k = n;
if(j < k) v.eb(mp(i, j));
}
}
pt(v.size());
for(auto g : v) {
int k = (n - g.fi - g.se + n) % n;
if(k == 0) k = n;
printf("%d %d %d\n", g.fi, g.se, k);
}
}
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt", "w", stdout);
int t = 1;
// t = rd();
// scanf("%d", &t);
// cin >> t;
while(t--)
solve();
PAUSE;
return 0;
} D - Gambling Monster
题意: 有一个转盘,每次转动得到 ~
(
是
的幂次)的概率分别给出。最开始你有一个数
,每次转动转盘得到一个数
,如果
,就令
,否则
不变,求使
,期望转动转盘的次数
题解: 设为转到
的概率,
表示从
出发到
的期望,
表示转动一次转盘
变大的概率,那么初始值
,我们考虑从后往前转移,可以得到这样的式子。$
y
S(x)
S(x) = \sum_{x \oplus y > x}p(y)
y
x
0
0
O(n\log{n})
O(n^2)
z > x
cdqFWT$去解决。
#include<bits/stdc++.h>
using namespace std;
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err () { cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...);}
#define ll long long
#define ull unsigned long long
#define LL __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define PII pair<ll, ll>
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define em emplace
#define mp(a,b) make_pair(a,b)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define PAUSE system("pause");
const double Pi = acos(-1.0);
const double eps = 1e-6;
const int maxn = 2e6 + 10;
const int maxm = 1e5 + 10;
const int mod = 1e9 + 7;
inline ll rd() {
ll f = 0; ll x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
if (f) x = -x;
return x;
}
void out(ll a) {if(a<0)putchar('-'),a=-a;if(a>=10)out(a/10);putchar(a%10+'0');}
#define pt(x) out(x),puts("")
inline void swap(ll &a, ll &b){ll t = a; a = b; b = t;}
inline void swap(int &a, int &b){int t = a; a = b; b = t;}
inline ll min(ll a, ll b){return a < b ? a : b;}
inline ll max(ll a, ll b){return a > b ? a : b;}
ll qpow(ll n,ll k,ll mod) {ll ans=1;while(k){if(k&1)ans=ans*n%mod;n=n*n%mod;k>>=1;}return ans%mod;}
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
ll n, a[maxn], p[maxn], s[maxn], sum, pre[maxn];
ll limit, lim, dp[maxn], A[maxn], B[maxn], ans[maxn];
ll add(ll a, ll b) {return a+b>=mod?a+b-mod:a+b;}
void FWTxor(ll *a, long long type) {
int i, j, k, x, y;
for (i = 1; i <= limit; i++)
for (j = 0; j < (1 << limit); j += 1 << i)
for (k = 0; k < (1 << i - 1); k++)
x = (a[j | k] + a[j | (1 << i - 1) | k]) * type % mod,
y = (a[j | k] - a[j | (1 << i - 1) | k] + mod) * type % mod,
a[j | k] = x, a[j | (1 << i - 1) | k] = y;
}
void Xor(ll *ans, ll *a, ll *b) {
FWTxor(a, 1);
FWTxor(b, 1);
for (int i = 0; i < (1 << limit); i++)
a[i] = 1ll * a[i] * b[i] % mod;
FWTxor(a, (mod + 1) >> 1);
for (int i = 0; i < (1 << limit); i++)
ans[i] = a[i];
}
void cdq(int l, int r) {
int mid = (l + r) >> 1;
if (l == r && l != n - 1){
dp[l] = ((dp[l] + 1) * qpow(s[l], mod - 2, mod) % mod - 1 + mod) % mod;
}
if (l >= r)
return;
cdq(mid + 1, r);
limit = 0;
while ((1 << limit) < mid - l + 1)
limit++;
for (int i = 0; i <= (1 << limit); i++)
A[i] = B[i] = ans[i] = 0;
for (int i = mid + 1; i <= r; i++) {
A[i - mid - 1] = p[i - l];
B[i - mid - 1] = dp[i] + 1;
}
Xor(ans, A, B);
for (int i = l; i <= mid; i++)
dp[i] = (dp[i] + ans[i - l]) % mod;
cdq(l, mid);
}
void solve(){
scanf("%lld", &n);
sum = 0;
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
sum = add(sum, a[i]);
s[i] = pre[i] = dp[i] = 0;
}
int k = 0;
while((1<<k)<n) k++;
int invSum = qpow(sum, mod - 2, mod);
for(int i = 0; i < n; i++) {
p[i] = a[i] * invSum % mod;
pre[i] = add(p[i], pre[i - 1]);
}
for(int i = 0; i < n; i++) {
for(int j = 0; j < k; j++) {
if(!((i>>j)&1)) {
s[i] = add(s[i], (pre[(1 << (j + 1)) - 1] - pre[(1 << j) - 1] + mod) % mod);
}
}
}
cdq(0, n - 1);
printf("%lld\n", dp[0]);
}
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt", "w", stdout);
int t = 1;
// t = rd();
scanf("%d", &t);
// cin >> t;
while(t--)
solve();
PAUSE;
return 0;
} F - Hamburger Steak
solved by Sstee1XD & lllllan. 2:07(+)
题意:
n块牛排,m个烤盘,每块牛排要烤分钟,而且最多可以分两次烤,只要时间达到
就行,输出最小花费时间时,每块牛排的烤制方案,按照时间顺序输出。
思路:
先考虑最优的方案,用总时间除以总盘数向上取整。此时,如果烤制最久的牛排需要的时间比这个想要的最优解要小的话,我们就可以直接开始放了,放到不够为止,再开下一个盘。因为需要最长的时间都能够符合,那么我把一块牛排拆成两份,按照顺序放在两个盘里,也不会有任何重合的部分。如果时间比最优解要大的话,就说明要满足这个最优解的话,切完这块牛排,放在不同的盘里会有重复的部分,不满足题目要求,所以把最优值取成这个最大值就是当前情况下的最优。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 7;
struct Steak {
int id;
ll t;
bool operator < (const Steak &a) const {
return t > a.t;
}
}s[N];
vector<tuple<int, ll, ll>> ans[N];
void run() {
int n;
ll m;
scanf("%d %lld", &n, &m);
ll sum = 0;
for (int i = 1; i <= n; ++i) {
scanf("%lld", &s[i].t);
s[i].id = i;
sum += s[i].t;
}
sort(s + 1, s + 1 + n);
ll now = (sum + m - 1) / m;
ll lst = m;
ll l = 0, r = 0;
int pos = 1;
now = max(now, s[1].t);
ll pan = now;
while (pos <= n) {
int aid = s[pos].id;
ll t = s[pos].t;
if (pan >= t) {
ans[aid].push_back(make_tuple(lst, l, l + t));
l += t;
pan -= t;
} else {
ans[aid].push_back(make_tuple(lst, l, l + pan));
t -= pan;
lst--;
l = 0;
pan = now;
ans[aid].push_back(make_tuple(lst, l, l + t));
l += t;
pan -= t;
}
if (pan == 0) {
lst--;
l = 0;
pan = now;
}
pos++;
}
for (int i = 1; i <= n; ++i) {
printf("%d", ans[i].size());
if (ans[i].size() == 2) swap(ans[i][0], ans[i][1]);
for (auto t : ans[i]) {
int a;
ll b, c;
tie(a, b, c) = t;
printf(" %d %lld %lld", a, b, c);
}
puts("");
}
}
int main() {
int t = 1;
while (t--) run();
return 0;
} G - Hasse Diagram
题意: 对于正整数,定义
为其所有因子组成的一张有向图,两个因子之间右边当且仅当他们的倍数是质数倍,定义
为属于
的边数,求
题解: 的范围来到了
,不难发现这题得用亚线性筛来解决。赛时我的思路就到这了
对于一个,我们枚举任意一个它的质因数,可得这样的式子
,我也不知道这式子是怎么推出来的,就枚举了几个数验证正确性
其中为
的因子个数,可以和
一起筛出来
改改板子就好了,知道了,则
,要知道改哪里还是需要自己手推一遍min_25筛,第一天学这东西现在还是云里雾里的。
#include<bits/stdc++.h>
using namespace std;
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err () { cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...);}
#define ll long long
#define ull unsigned long long
#define LL __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define PII pair<ll, ll>
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define em emplace
#define mp(a,b) make_pair(a,b)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define PAUSE system("pause");
const double Pi = acos(-1.0);
const double eps = 1e-10;
const int maxn = 1e6 + 10;
const int maxm = 6e2 + 10;
const int mod = 1145140019;
inline ll rd() {
ll f = 0; ll x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
if (f) x = -x;
return x;
}
void out(ll a) {if(a<0)putchar('-'),a=-a;if(a>=10)out(a/10);putchar(a%10+'0');}
#define pt(x) out(x),puts("")
inline void swap(ll &a, ll &b){ll t = a; a = b; b = t;}
inline void swap(int &a, int &b){int t = a; a = b; b = t;}
inline ll min(ll a, ll b){return a < b ? a : b;}
inline ll max(ll a, ll b){return a > b ? a : b;}
ll qpow(ll n,ll k,ll mod) {ll ans=1;while(k){if(k&1)ans=ans*n%mod;n=n*n%mod;k>>=1;}return ans%mod;}
ll prime[maxn],num,sp1[maxn];
ll n,Sqr,tot,g1[maxn],w[maxn],ind1[maxn],ind2[maxn];
bool flag[maxn];
void pre(int n){
flag[1] = 1;
for(int i = 1; i <= n; i++){
if(!flag[i]) {
prime[++num] = i;
sp1[num] = (sp1[num - 1] + 1) % mod;
}
for(int j = 1; j <= num && prime[j] * i <= n; j++) {
flag[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
}
PII S(ll x,int y) {
if(prime[y] >= x) return mp(0, 0);
ll k = x <= Sqr ? ind1[x] : ind2[n / x];
ll ansf = (g1[k] - sp1[y] + mod) % mod;
ll ansd = (g1[k] - sp1[y] + g1[k] - sp1[y] + 2ll * mod) % mod;
for(int i = y + 1; i <= num && prime[i] * prime[i] <= x; i++) {
ll pe = prime[i];
for(int e = 1; pe <= x; e++, pe = pe * prime[i]){
PII tmp = S(x / pe, i);
ansf = (ansf + (e + 1) * tmp.fi % mod + e * (tmp.se + (e > 1)) % mod) % mod;
ansd = (ansd + (e + 1) * (tmp.se + (e > 1))% mod) % mod;
}
}
return mp(ansf, ansd);
}
void solve() {
scanf("%lld", &n);
Sqr = sqrt(n);
pre(Sqr);
tot = 0;
for(ll i = 1; i <= n; ){
ll j = n / (n / i);
w[++tot] = n / i;
g1[tot] = (w[tot] - 1 + mod) % mod;
if(n / i <= Sqr) ind1[n / i] = tot;
else ind2[n / (n / i)] = tot;
i = j + 1;
}
for(int i = 1; i <= num; i++) {
for(int j = 1; j <= tot && prime[i] * prime[i] <= w[j]; j++) {
ll k = w[j] / prime[i] <= Sqr ? ind1[w[j] / prime[i]] : ind2[n / (w[j] / prime[i])];
g1[j] -= (g1[k] - sp1[i - 1] + mod) % mod;
g1[j] %= mod;
if(g1[j] < 0) g1[j] += mod;
}
}
printf("%lld\n", (S(n, 0).fi) % mod);
}
int main() {
int t;
scanf("%d", &t);
while(t--)
solve();
return 0;
} H - Hopping Rabbit
题意:
二维平面,一些矩阵块不能进,你每次会上下左右选择一个方向,移动个单位。找一个起点
,使得不管怎么走都不会走到非法矩阵块里。
思路:
可以看作人不动,矩阵块向人移动。因为每次移动的距离是一定的,所以在一个的矩阵里就可以表示出所有情况了。把所有非法矩阵往这个起点矩阵里移动,剩下的没有被覆盖掉的点就是合法起点。注意非法矩阵可能不会完全移动到起点矩阵里面,所以得上下左右都移动一下,把所有能被覆盖到的点都覆盖掉。
然后这道题就变成了在一个大矩阵中求没有被覆盖掉的矩阵。用扫描线维护下二维偏序就行。扫到一个矩阵的左边界时,将这块对应的值都
,扫到右边界时,都
,找区间最小值是否有为
的,如果有就说明存在合法的点,找到其中一个输出即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e5 + 7;
int n, d;
const int inf = 0x3f3f3f3f;
struct ret{
int x1, y1, x2, y2;
ret(int _x1 = 0, int _y1 = 0, int _x2 = 0, int _y2 = 0) {
x1 = _x1; x2 = _x2;
y1 = _y1; y2 = _y2;
}
}a[maxn];
vector<ret> add[maxn], del[maxn];
struct Seg {
int t[maxn << 2], tag[maxn << 2];
void up(int id) {
t[id] = min(t[id << 1], t[id << 1 | 1]);
}
void build(int id, int l, int r) {
t[id] = 0;
tag[id] = 0;
if (l == r) return;
int mid = l + r >> 1;
build(id << 1, l, mid);
build(id << 1 | 1, mid + 1, r);
}
void down(int id) {
if (!tag[id]) return;
tag[id << 1 | 1] += tag[id];
tag[id << 1] += tag[id];
t[id << 1] += tag[id];
t[id << 1 | 1] += tag[id];
tag[id] = 0;
}
void modify(int id, int l, int r, int ql, int qr, int v) {
if (l >= ql && r <= qr) {
tag[id] += v;
t[id] += v;
return;
}
down(id);
int mid = l + r >> 1;
if (ql <= mid) modify(id << 1, l, mid, ql, qr, v);
if (qr > mid) modify(id << 1 | 1, mid + 1, r, ql, qr, v);
up(id);
}
int query(int id, int l, int r) {
if (t[id]) return -1;
if (l == r) return l;
down(id);
int mid = l + r >> 1;
if (!t[id << 1]) return query(id << 1, l, mid);
else return query(id << 1 | 1, mid + 1, r);
}
}seg;
void run() {
scanf("%d %d", &n, &d);
int num = 0;
for(int i = 1, X1, X2, Y1, Y2; i <= n; i++) {
scanf("%d %d %d %d", &X1, &Y1, &X2, &Y2);
int tmpx = 0, tmpy = 0;
if(X1 > 0) tmpx = -1 * X1 / d;
if(X1 < 0) tmpx = (abs(X1) + d - 1) / d;
if(Y1 > 0) tmpy = -1 * Y1 / d;
if(Y1 < 0) tmpy = (abs(Y1) + d - 1) / d;
X1 = X1 + tmpx * d;
Y1 = Y1 + tmpy * d;
X2 = X2 + tmpx * d;
Y2 = Y2 + tmpy * d;
if(X2 > d && Y2 > d) {
a[++num] = ret(X1, Y1, d, d);
// right
a[++num] = ret(0, Y1, min(d, X2 - d), d);
// top
a[++num] = ret(X1, 0, d, min(d, Y2 - d));
// right - top
a[++num] =ret(0, 0, min(d, X2 - d), min(d, Y2 - d));
}
else if(X2 > d) {
a[++num] = ret(X1, Y1, d, Y2);
// right
a[++num] = ret(0, Y1, min(d, X2 - d), Y2);
}
else if(Y2 > d) {
a[++num] = ret(X1, Y1, X2, d);
// top
a[++num] = ret(X1, 0, X2, min(d, Y2 - d));
}
else {
a[++num] = ret(X1, Y1, X2, Y2);
}
}
for(int i = 1; i <= num; i++) {
add[a[i].x1].push_back(a[i]);
del[a[i].x2].push_back(a[i]);
}
// 输入y2-1,因为要加个0.5,所以上边界的点是合法的
seg.build(1, 0, d - 1);
for (int i = 0; i < d; ++i) {
for (auto v : add[i]) {
seg.modify(1, 0, d - 1, v.y1, v.y2 - 1, 1);
}
for (auto v : del[i]) {
seg.modify(1, 0, d - 1, v.y1, v.y2 - 1, -1);
}
int y = seg.query(1, 0, d - 1);
if (y >= 0) {
puts("YES");
printf("%d %d\n", i, y);
return;
}
}
puts("NO");
}
int main() {
run();
return 0;
} I - Intervals on the Ring
题意: 给出一些不相交的区间,要求你构造出一些区间。使得你构造的区间取交集,等于题目给的区间取并集。构造不出输出-1
注意:如果区间其中
,这种对应的区间应该是
思路: 首先,一定是构造的出来的。无论题目区间取并集一共有多少个连续的区间,都一定可以构造的出。
题目很友好地已经保证了区间不相交。只需要将所有区间,对其左端点进行排序,然后依次输出即可
举个栗子,有如下几个区间
我们依次输出
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 7;
int n, m;
struct node {
int l, r;
node () {}
node (int l, int r) : l(l), r(r) {}
friend bool operator<(const node& A, const node& B) {
return A.l < B.l;
}
} nodes[maxn];
void run() {
scanf("%d%d", &n, &m);
for (int i = 1, l, r; i <= m; ++i) {
scanf("%d%d", &l, &r);
nodes[i] = node(l, r);
}
sort(nodes + 1, nodes + m + 1);
printf("%d\n", m);
for (int i = 1; i <= m; ++i) {
printf("%d %d\n", nodes[i].l, i == 1 ? nodes[m].r : nodes[i - 1].r);
}
}
int main() {
int t = 1;
scanf("%d", &t);
while (t--) run();
return 0;
} J - Defend Your Country
题意: 在一个无向连通图中,每个点都有点权。如果一个连通分量中点的个数是偶数,那么这些点的权值都不变;如果是奇数,则对这些权值取负。
你可以删除一些边,改变图的连通性,以获得最大的权值和。
思路: 我们的目的其实就是,删除一些边,使得有足够多的偶数点的连通分量。
关键信息:无向连通图。即输入给出的原图,已经是连通的,如果一共有偶数个点,那就无需做任何删除。
如果是奇数:那么最理想的是不是就是拆分成若干个偶数点的连通分量,以及一个落单的孤立点。于是我们想办法,要怎么确定这个 孤儿 孤立点。
- 割点:假如我们分离出一个割点,改变了原图的连通性,那么我们还要检查剩下的连通分量中点的个数。 —— 如果还存在奇数个点的连通分量,那么这次分离,似乎是不划算的。
- 非割点:我随意分离出去,并不影响剩下的图的连通性,反倒将奇数个点扭转成了偶数个点,稳赚不赔,找一个权值最小的非割点似乎是最划算的。
讨论,是不是在任何奇数点的情况下,都只需要删除一个点,就能获得最优解。

嘴笨说不清楚,就先附上别的聚聚的题解了,其题解链接
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
int cnt, head[maxn];
struct edge {
int v, next;
edge() {}
edge(int v, int next) : v(v), next(next) {}
} e[maxn << 2];
void add(int u, int v) {
e[++cnt] = edge(v, head[u]), head[u] = cnt;
e[++cnt] = edge(u, head[v]), head[v] = cnt;
}
int n, m;
int a[maxn];
int ans, tot;
int num[maxn];
int low[maxn];
int dfn[maxn];
void dfs(int u, int fa) {
num[u] = 1;
dfn[u] = low[u] = ++tot;
int flag = 1;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if (v == fa) continue;
if (!dfn[v]) {
dfs(v, u);
num[u] += num[v];
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u] && num[v] & 1) flag = 0;
} else
low[u] = min(low[u], dfn[v]);
}
if (flag) ans = min(ans, a[u]);
}
void run() {
scanf("%d%d", &n, &m);
ll sum = 0;
cnt = tot = 0;
for (int i = 1; i <= n; ++i) head[i] = num[i] = low[i] = dfn[i] = 0;
for (int i = 1; i <= n; ++i) scanf("%d", a + i), sum += a[i];
for (int i = 1, u, v; i <= m; ++i) {
scanf("%d%d", &u, &v);
add(u, v);
}
if (n % 2 == 0) return (void)(printf("%lld\n", sum));
ans = 1e9 + 7;
dfs(1, 0);
printf("%lld\n", sum - 2 * ans);
}
int main() {
int _ = 1;
scanf("%d", &_);
while (_--) run();
return 0;
}
海康威视公司氛围 916人发布
查看8道真题和解析