[赛记] 暑假集训CSP提高模拟20 21
Kanon 40pts
签到题,但是不会,所以打了暴力;
正解时考虑相邻两个雪球,只有两种情况:它们的覆盖区间有交集或无交集,那么如果我们找出了无交集的最后一天,我们就很容易判断剩下的一堆雪该被谁拿走,于是我们二分找出这一天即可;赛时确实想不到二分
时间复杂度:$ \Theta(n \log n) $;
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n, q;
long long a[500005];
long long w[500005];
long long sum[500005];
long long an[500005];
long long ma[500005], mi[500005];
namespace BIT{
long long tr[2][5000005];
inline int lowbit(int x) {
return x & (-x);
}
void add(int pos, long long d) {
for (int i = pos; i <= q; i += lowbit(i)) {
tr[0][i] = max(tr[0][i], d);
tr[1][i] = min(tr[1][i], d);
}
}
long long ask(int s, int pos) {
if (s == 0) {
long long ans = -0x3f3f3f3f3f3f3f3f;
for (int i = pos; i; i -= lowbit(i)) {
ans = max(ans, tr[0][i]);
}
return ans;
} else {
long long ans = 0x3f3f3f3f3f3f3f3f;
for (int i = pos; i; i -= lowbit(i)) {
ans = min(ans, tr[1][i]);
}
return ans;
}
}
}
using namespace BIT;
bool ck(int x, long long s) {
long long mma = ma[x];
long long mmi = mi[x];
if (mmi > 0) return mma <= s;
else return mma - mmi <= s;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= q; i++) {
cin >> w[i];
}
for (int i = 1; i <= n; i++) {
tr[0][i] = -0x3f3f3f3f3f3f3f3f;
tr[1][i] = 0x3f3f3f3f3f3f3f3f;
}
long long mma = 0;
long long mmi = 0;
for (int i = 1; i <= q; i++) {
sum[i] = sum[i - 1] + w[i];
mma = max(mma, sum[i]);
mmi = min(mmi, sum[i]);
add(i, sum[i]);
ma[i] = max(0ll, ask(0, i));
mi[i] = min(0ll, ask(1, i));
}
an[1] += (-mmi);
an[n] += mma;
for (int i = 1; i <= n - 1; i++) {
int l = 1;
int r = q;
int ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if (ck(mid, a[i + 1] - a[i])) {
l = mid + 1;
ans = mid;
} else {
r = mid - 1;
}
}
mma = ma[ans];
mmi = mi[ans];
ans++;
an[i + 1] += (-mmi);
an[i] += mma;
if (ans > q) continue;
if (w[ans] < 0) {
an[i + 1] -= (-mmi);
an[i + 1] += (a[i + 1] - a[i] - mma);
} else {
an[i] -= mma;
an[i] += (a[i + 1] - a[i] + mmi);
}
}
for (int i = 1; i <= n; i++) {
cout << an[i] << '\n';
}
return 0;
}
黎明与萤火 0pts
签到题爆零。。。
赛时打的是正解,但没有判掉已经确定的答案导致错误;
考虑从下往上找(因为从上往下可能会使原来根节点和数个子节点都是偶数,结果删了根节点后数个子节点都是奇数导致不能再选,而从下往上可以反过来甚至避免这个问题,总的来说就是尽量选一个点,使其影响的点较少),那么我们可以采用类似拓扑排序的思路,开一个优先队列,重载运算符使其将深度大的且是偶数的点放在上面,每次取出队头是判断一下现在的奇偶以及被计算进答案的情况,然后将其符合条件且未被计算进答案的点放进去,完事以后判断一下即可;
时间复杂度:每个点至多会进队其度数次(这也是正确性的保证),但是总的遍历次数是度数次的,乘上优先队列的复杂度,那么总的时间复杂度为 $ \Theta(n \log n) $;
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
int n;
vector<int> v[500005];
vector<int> ans;
int d[500005], dep[500005], dd[500005];
bool vis[500005];
struct sss{
int x;
bool operator <(const sss &A) const {
return dep[x] < dep[A.x];
}
bool operator >(const sss &A) const {
return dep[x] < dep[A.x];
}
};
priority_queue<sss> q;
void dfs(int x, int fa) {
dep[x] = dep[fa] + 1;
for (int i = 0; i < v[x].size(); i++) {
int u = v[x][i];
if (u == fa) continue;
dfs(u, x);
}
}
bool solve() {
while(!q.empty()) {
while(!q.empty() && (d[q.top().x] % 2 != 0 || vis[q.top().x])) q.pop();
if (q.empty()) break;
sss tt = q.top();
q.pop();
int t = tt.x;
vis[t] = true;
d[t] = 0;
ans.push_back(t);
for (int i = 0; i < v[t].size(); i++) {
int u = v[t][i];
if (vis[u]) continue;
d[u]--;
if (d[u] % 2 == 0) {
q.push(sss{u});
}
}
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) return false;
}
return true;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
int x;
for (int i = 1; i <= n; i++) {
cin >> x;
if (x == 0) continue;
v[i].push_back(x);
v[x].push_back(i);
d[x]++;
d[i]++;
}
dfs(1, 0);
for (int i = 1; i <= n; i++) {
if (d[i] % 2 == 0) {
q.push(sss{i});
}
}
if (solve()) {
cout << "YES" << '\n';
for (int i = 0; i < ans.size(); i++) {
cout << ans[i] << '\n';
}
} else {
cout << "NO";
}
return 0;
}
Darling Dance 10pts
赛时没看这道题,输出了个0就走了,拿了10pts
这个题把最短路DAG建出来就行,然后从1开始对其BFS(DFS也行),选k条边即可;
对于最短路DAG,只需在最短路成功更新时记录一下从哪里更新的即可(即其前驱节点);
注意k=0的情况;
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
int n, m, k;
struct sss{
int t, ne, w;
}e[2000005];
int h[2000005], cnt;
void add(int u, int v, int ww) {
e[++cnt].t = v;
e[cnt].ne = h[u];
h[u] = cnt;
e[cnt].w = ww;
}
int dis[500005];
bool vis[500005];
int fr[500005], id[500005];
vector<int> v[500005];
int w(int x, int y) {
if (x % y == 0) return x / y;
else return x / y + 1;
}
void dij(int x) {
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[x] = 0;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
q.push({0, x});
while(!q.empty()) {
int xu = q.top().second;
q.pop();
if (vis[xu]) continue;
vis[xu] = true;
for (int i = h[xu]; i; i = e[i].ne) {
int u = e[i].t;
if (dis[u] > dis[xu] + e[i].w) {
dis[u] = dis[xu] + e[i].w;
fr[u] = xu;
id[u] = w(i, 2);
q.push({dis[u], u});
}
}
}
}
queue<int> p;
int ans[500005], o;
void bfs() {
p.push(1);
while(!p.empty()) {
int t = p.front();
p.pop();
for (int i = 0; i < v[t].size(); i++) {
int u = v[t][i];
if (o == k) return;
p.push(u);
ans[++o] = id[u];
}
}
}
int main() {
cin >> n >> m >> k;
int x, y, w;
for (int i = 1; i <= m; i++) {
cin >> x >> y >> w;
add(x, y, w);
add(y, x, w);
}
dij(1);
for (int i = 1; i <= n; i++) {
v[fr[i]].push_back(i);
}
bfs();
cout << o << '\n';
for (int i = 1; i <= o; i++) {
cout << ans[i] << ' ';
}
return 0;
}
Non-breath oblige 10pts
原题:Luogu P5524 [Ynoi2012] NOIP2015 充满了希望
赛时用线段树打暴力拿了10pts;
考虑正解,不难发现对于一个3操作,只有在它前面且与其最近的2操作才能对其产生贡献;
于是对于这些操作,我们开一个在1到n的范围内的线段树,叶子节点的值代表这个值最后一次被2操作修改是什么时候,然后对于3操作进行单点查询,并且维护一个pre数组,记录一下这个3操作是被哪个2操作修改的,并且记录一下值即可;
然后我们发现对于一个询问,我们要找出pre值大于等于l且小于等于r的值的和;
那么这就是一个二维偏序问题,用树状数组维护即可;
在题库中要用快读,Luogu不用
点击查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
int n, m, qq;
#define FI(n) FastIO::read(n)
#define FO(n) FastIO::write(n)
#define Flush FastIO::Fflush()
namespace FastIO {
const int SIZE=1<<16;
char buf[SIZE],obuf[SIZE],str[60];
int bi=SIZE,bn=SIZE,opt;
inline int read(register char *s) {
while(bn){
for(;bi<bn&&buf[bi]<=' ';bi=-~bi);
if(bi<bn)break;
bn=fread(buf,1,SIZE,stdin);
bi&=0;
}
register int sn=0;
while(bn){
for(;bi<bn&&buf[bi]>' ';bi=-~bi)s[sn++]=buf[bi];
if(bi<bn)break;
bn=fread(buf,1,SIZE,stdin);
bi&=0;
}
s[sn]&=0;
return sn;
}
inline bool read(register int &x){
int n=read(str),bf=0;
if(!n)return 0;
register int i=0;
(str[i]=='-')&&(bf=1,i=-~i);
(str[i]=='+')&&(i=-~i);
for(x=0;i<n;i=-~i)x=(x<<3)+(x<<1)+(str[i]^48);
bf&&(x=~x+1);
return 1;
}
inline bool read(register long long &x) {
int n=read(str),bf=1;
if(!n)return 0;
register int i=0;
(str[i]=='-')&&(bf=-1,i=-~i);
for(x=0;i<n;i=-~i)x=(x<<3)+(x<<1)+(str[i]^48);
(bf<0)&&(x=~x+1);
return 1;
}
inline void write(register int x) {
if(!x)obuf[opt++]='0';
else{
(x<0)&&(obuf[opt++]='-',x=~x+1);
register int sn=0;
while(x)str[sn++]=x%10+'0',x/=10;
for(register int i=sn-1;i>=0;i=~-i)obuf[opt++]=str[i];
}
(opt>=(SIZE>>1))&&(fwrite(obuf,1,opt,stdout),opt&=0);
}
inline void write(register long long x) {
if(!x)obuf[opt++]='0';
else{
(x<0)&&(obuf[opt++]='-',x=~x+1);
register int sn=0;
while(x)str[sn++]=x%10+'0',x/=10;
for(register int i=sn-1;i>=0;i=~-i)obuf[opt++]=str[i];
}
(opt>=(SIZE>>1))&&(fwrite(obuf,1,opt,stdout),opt&=0);
}
inline void write(register unsigned long long x){
if(!x)obuf[opt++]='0';
else{
register int sn=0;
while(x)str[sn++]=x%10+'0',x/=10;
for(register int i=sn-1;i>=0;i=~-i)obuf[opt++]=str[i];
}
(opt>=(SIZE>>1))&&(fwrite(obuf,1,opt,stdout),opt&=0);
}
inline void write(register char x) {
obuf[opt++]=x;
(opt>=(SIZE>>1))&&(fwrite(obuf,1,opt,stdout),opt&=0);
}
inline void Fflush(){
opt&&fwrite(obuf,1,opt,stdout);
opt&=0;
}
};
struct sss{
int l, r, id;
bool operator <(const sss &A) const {
return l > A.l;
}
}q[1000005];
int L, R, x, y, s;
int pre[1000005], val[1000005];
long long ans[1000005];
struct sas{
int pre, pos, val;
bool operator <(const sas &A) const {
return pre < A.pre;
}
};
priority_queue<sas> p;
namespace seg{
inline int ls(int x) {
return x << 1;
}
inline int rs(int x) {
return x << 1 | 1;
}
struct sss{
int l, r, val, lz;
}tr[4000005];
inline void push_down(int id) {
if (tr[id].lz != -1) {
tr[ls(id)].lz = tr[id].lz;
tr[rs(id)].lz = tr[id].lz;
tr[ls(id)].val = tr[id].lz;
tr[rs(id)].val = tr[id].lz;
tr[id].lz = -1;
}
}
void bt(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
tr[id].lz = -1;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
bt(ls(id), l, mid);
bt(rs(id), mid + 1, r);
}
void add(int id, int l, int r, int d) {
if (tr[id].l >= l && tr[id].r <= r) {
tr[id].lz = d;
tr[id].val = d;
return;
}
push_down(id);
int mid = (tr[id].l + tr[id].r) >> 1;
if (l <= mid) add(ls(id), l, r, d);
if (r > mid) add(rs(id), l, r, d);
}
int ask(int id, int pos) {
if (tr[id].l == tr[id].r) return tr[id].val;
push_down(id);
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) return ask(ls(id), pos);
else return ask(rs(id), pos);
}
}
namespace BIT{
inline int lowbit(int x) {
return x & (-x);
}
long long tr[20000005];
void add(int pos, int d) {
for (int i = pos; i <= m; i += lowbit(i)) tr[i] += d;
}
long long ask(int pos) {
long long ans = 0;
for (int i = pos; i; i -= lowbit(i)) ans += tr[i];
return ans;
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
FI(n);
FI(m);
FI(qq);
seg::bt(1, 1, n);
for (int i = 1; i <= m; i++) {
FI(s);
if (s == 1) {
FI(x);
FI(y);
int xx = seg::ask(1, x);
int yy = seg::ask(1, y);
seg::add(1, x, x, yy);
seg::add(1, y, y, xx);
} else if (s == 2) {
FI(L);
FI(R);
FI(x);
seg::add(1, L, R, i);
val[i] = x;
} else if (s == 3) {
FI(x);
pre[i] = seg::ask(1, x);
val[i] = val[pre[i]];
p.push({pre[i], i, val[i]});
}
}
for (int i = 1; i <= qq; i++) {
FI(q[i].l);
FI(q[i].r);
q[i].id = i;
}
sort(q + 1, q + 1 + qq);
for (int i = 1; i <= qq; i++) {
while(!p.empty()) {
sas t = p.top();
if (t.pre >= q[i].l) {
BIT::add(t.pos, t.val);
p.pop();
} else break;
}
ans[q[i].id] = BIT::ask(q[i].r) - BIT::ask(q[i].l - 1);
}
for (int i = 1; i <= qq; i++) {
FO(ans[i]);
FO('\n');
}
Flush;
return 0;
}
最近比赛打的真是越来越CD了。。。