Preface
这场打的也挺好的,前中期稳定出题,后期还和徐神接力写了一个K
最后乱猜还猜对了H题的结论,可惜因为常数以及三分的bound等问题赛事没过
最后10题舒服下班
A. Organizing SWERC
签到,话说外国场的A基本都是签到啊
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int t,n,b,d,bkt[15];
int main()
{
for (scanf("%d",&t);t;--t)
{
RI i; for (i=1;i<=10;++i) bkt[i]=-1;
for (scanf("%d",&n),i=1;i<=n;++i)
scanf("%d%d",&b,&d),bkt[d]=max(bkt[d],b);
int ans=0; for (i=1;i<=10;++i)
if (bkt[i]==-1) { ans=-1; break; } else ans+=bkt[i];
if (ans==-1) puts("MOREPROBLEMS"); else printf("%d\n",ans);
}
return 0;
}
B. Drone Photo
思博题,大力找出所有合法矩形的共性就很好统计答案了
对于一个矩形的四个顶点,我们定义一个顶点被标记当且仅当它的权值恰好在与它相邻的两个顶点的权值之间
考虑所有可能的合法矩形,我们发现它们都恰好有两个标记点,且不论标记点的分布是对角还是相邻都是合法的
那么我们可以直接统计出所有点作为标记点的数量,最后除以\(2\)就是答案了
上面这个也很好统计,对于每个数求出它所在的行/列有多少个数小于/大于它即可快速计算
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cctype>
#define RI register int
#define CI const int&
#define Tp template <typename T>
using namespace std;
const int N=1505;
int n,a[N][N],r[N*N],c[N*N];
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
char Fin[S],*A,*B;
public:
Tp inline void read(T& x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
#undef tc
}F;
int main()
{
RI i,j; for (F.read(n),i=1;i<=n;++i)
for (j=1;j<=n;++j) F.read(a[i][j]);
for (i=1;i<=n;++i)
{
vector <int> tmp;
for (j=1;j<=n;++j) tmp.push_back(a[i][j]);
sort(tmp.begin(),tmp.end());
for (j=0;j<n;++j) r[tmp[j]]=j;
}
for (j=1;j<=n;++j)
{
vector <int> tmp;
for (i=1;i<=n;++i) tmp.push_back(a[i][j]);
sort(tmp.begin(),tmp.end());
for (i=0;i<n;++i) c[tmp[i]]=i;
}
long long ans=0; for (i=1;i<=n*n;++i)
ans+=1LL*r[i]*(n-1-c[i])+1LL*c[i]*(n-1-r[i]);
return printf("%lld",ans/2LL),0;
}
C. Il Derby della Madonnina
很套路的一个题,考虑两个事件\(i,j\)能连着进行进行的充要条件为:
- \(t_i<t_j\)
- \((t_j-t_i)\times v\ge |a_i-a_j|\)
拆掉下面那个式子的绝对值我们得到:
- \(v\times t_i-a_i\le v\times t_j-a_j\)
- \(v\times t_i+a_i\le v\times t_j+a_j\)
这里乍一看是一个三维的LIS,但仔细分析一下会发现下面两个式子已经天然满足了\(t_i<t_j\)(直接把两个不等式相加即可)
因此问题等价于求一个二维的LIS,给其中一维排序后树状数组处理即可
注意要把初始状态\((0,0)\)看作一个强制选的事件来处理,刚开始没判好WA了两发
#include<cstdio>
#include<iostream>
#include<utility>
#include<algorithm>
#define int long long
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=200005,INF=1e9;
int n,m,v,t[N],a[N],rst[N],f[N],ans; pi w[N];
class Tree_Array
{
private:
int bit[N];
public:
#define lowbit(x) (x&-x)
inline void init(void)
{
for (RI i=0;i<=m;++i) bit[i]=-INF;
}
inline int get(RI x,int ret=-INF)
{
for (;x;x-=lowbit(x)) ret=max(ret,bit[x]); return ret;
}
inline void add(RI x,CI y)
{
for (;x<=m;x+=lowbit(x)) bit[x]=max(bit[x],y);
}
#undef lowbit
}BIT;
signed main()
{
RI i; for (scanf("%lld%lld",&n,&v),i=1;i<=n;++i) scanf("%lld",&t[i]);
for (i=1;i<=n;++i) scanf("%lld",&a[i]),w[i]=pi(v*t[i]-a[i],v*t[i]+a[i]);
for (w[n+1]=pi(0,0),sort(w+1,w+n+2),i=1;i<=n+1;++i) rst[i]=w[i].se;
sort(rst+1,rst+n+2); m=unique(rst+1,rst+n+2)-rst-1;
for (i=1;i<=n+1;++i) if (w[i].fi==0&&w[i].se==0) break;
BIT.init(); BIT.add(lower_bound(rst+1,rst+m+1,0)-rst,0);
for (++i;i<=n+1;++i)
{
int x=lower_bound(rst+1,rst+m+1,w[i].se)-rst;
f[i]=BIT.get(x)+1; BIT.add(x,f[i]); ans=max(ans,f[i]);
}
return printf("%lld",ans),0;
}
D. Ice Cream Shop
首先可以通过二分快速求出距离每个房子最近的冰激凌店的距离
不难发现对于一个房子,新建的冰激凌店坐标要在一个开区间内才能得到其贡献,得到所有区间后排序处理即可
#include <bits/stdc++.h>
using llsi = long long signed int;
constexpr int $n = 200000;
int n, m;
int p[$n], x[$n];
struct Ev { int pos, delta; };
int main() {
std::ios::sync_with_stdio(false);
std::cin >> n >> m;
for(int i = 0; i < n; ++i) std::cin >> p[i];
for(int i = 0; i < m; ++i) std::cin >> x[i];
std::sort(x, x + m);
std::vector<Ev> ev;
for(int i = 0; i < n; ++i) {
int pos = 100 * i;
int s = std::lower_bound(x, x + m, pos) - x;
int d = 0x7fffffff;
if(s < m) d = std::min(d, x[s] - pos);
if(s > 0) d = std::min(d, pos - x[s - 1]);
ev.push_back({pos - d, +p[i]});
ev.push_back({pos + d, -p[i]});
}
std::sort(ev.begin(), ev.end(), [](const Ev &a, const Ev &b) {
return a.pos < b.pos;
});
llsi ans = 0, cur = 0;
int pre = 0x80000000;
for(auto [pos, delta]: ev) {
if(pos > pre && cur > ans) ans = cur;
pre = pos; cur += delta;
}
std::cout << ans << char(10);
return 0;
}
E. Evolution of Weasels
ORZ徐神单切了此题
由于操作可逆,考虑将两个串都先压缩成最简表达形式,再判断是否相等
手玩后发现相邻的\(AB\)可以交换,相邻的\(BC\)也可以交换,但相邻的\(AC\)就不能换
因此可以省略掉序列中所有的\(B\),若个数为偶数则可以全部删除,否则可以只剩下一个放在任意位置,不妨统一移动到最后
而剩下的\(A,C\)只要出现两个相邻的相同的字符就可以删去,拿个栈模拟一下即可
#include <bits/stdc++.h>
std::vector<char> shrink(const std::string &s) {
std::vector<char> res;
int Bcnt = 0;
for(auto ch: s) {
if(ch == 'B') Bcnt += 1;
else {
if(res.size() && ch == res.back()) res.pop_back();
else res.push_back(ch);
}
}
if(Bcnt & 1) res.push_back('B');
return res;
}
int main(void) {
std::ios::sync_with_stdio(false);
int t; std::cin >> t; while(t--) {
std::string x, y;
std::cin >> x >> y;
std::cout << (shrink(x) == shrink(y) ? "YES\n" : "NO\n");
}
return 0;
}
F. Bottle Arrangements
签到构造题
不难发现令\(R=\max r_i,W=\max w_i\),直接放\(R\)个R
,\(W\)个W
就可以满足所有限制了
剩下有多余的空位就随便放即可,如果不够放就无解
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int t,n,m,mr,mw,r,w;
int main()
{
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d%d",&n,&m),mr=mw=0,i=1;i<=m;++i)
scanf("%d%d",&r,&w),mr=max(mr,r),mw=max(mw,w);
if (mr+mw>n) { puts("IMPOSSIBLE"); continue; }
for (i=1;i<=mr;++i) putchar('R');
for (i=1;i<=n-mr;++i) putchar('W');
putchar('\n');
}
return 0;
}
G. Round Table
防AK题,做不了一点
H. Pandemic Restrictions
刚开始队友叫我去写个退火,但我感觉时间不够了就让祁神Rush个四层三分的做法,没想到最后还真是对的
做法就是两层三分找绿色点的最优位置,至于对于三个点的红色点的最优位置也可以通过三分求解
但由于常数以及卡精度不熟练等问题最后实测跑的有点慢,祁神准备赛后再卡一波的说
不过比赛的时候徐神就提出了直接用几何方法算最后红色点的位置的做法,如果这里可以快速算的话复杂度就很对了
I. Antennas
纯套路题,拆绝对值和拆\(\min\)都是很经典的trick了
对于式子\(|i-j|\le \min(p_i,p_j)\),不妨设\(i<j\),则需要同时满足以下条件:
- \(j\in [i+1,i+p_i]\)
- \(j-p_j\le i\)
考虑BFS求解的过程,假设当前从\(i\)点进行扩展,考虑怎么快速求出所有符合条件的\(j\)点
第一个式子告诉我们合法的\(j\)的下标在某个区间中,则不难想到用线段树
每个节点维护\(j-p_j\)的最小值,然后递归查询,如果当前区间不合法就直接退出,否则一路递归到叶子节点把合法的点删除并加入BFS的队列中
由于BFS的性质,每个点只会入队一次,因此线段树部分的复杂度也是对的
#include<cstdio>
#include<iostream>
#include<queue>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,INF=1e9;
int T,n,s,t,p[N],vis[N],dis[N]; queue <int> q;
class Segment_Tree
{
private:
int mn[N<<2],mx[N<<2];
inline void pushup(CI now)
{
mn[now]=min(mn[now<<1],mn[now<<1|1]);
mx[now]=max(mx[now<<1],mx[now<<1|1]);
}
public:
#define TN CI now=1,CI l=1,CI r=n
#define LS now<<1,l,mid
#define RS now<<1|1,mid+1,r
inline void build(TN)
{
if (l==r)
{
if (l==s) mn[now]=INF,mx[now]=-INF;
else mn[now]=l-p[l],mx[now]=l+p[l];
return;
}
int mid=l+r>>1; build(LS); build(RS); pushup(now);
}
inline void query_min(CI beg,CI end,CI idx,TN)
{
if (beg>end) return; if (mn[now]>idx) return;
if (l==r) return mn[now]=INF,mx[now]=-INF,dis[l]=dis[idx]+1,q.push(l);
int mid=l+r>>1; if (beg<=mid) query_min(beg,end,idx,LS); if (end>mid) query_min(beg,end,idx,RS); pushup(now);
}
inline void query_max(CI beg,CI end,CI idx,TN)
{
if (beg>end) return; if (mx[now]<idx) return;
if (l==r) return mn[now]=INF,mx[now]=-INF,dis[l]=dis[idx]+1,q.push(l);
int mid=l+r>>1; if (beg<=mid) query_max(beg,end,idx,LS); if (end>mid) query_max(beg,end,idx,RS); pushup(now);
}
#undef TN
#undef LS
#undef RS
}SEG;
int main()
{
for (scanf("%d",&T);T;--T)
{
RI i; for (scanf("%d%d%d",&n,&s,&t),i=1;i<=n;++i) scanf("%d",&p[i]),vis[i]=0;
q.push(s); dis[s]=0; SEG.build();
while (!q.empty())
{
int now=q.front(); q.pop();
if (vis[now]) continue; vis[now]=1;
SEG.query_min(now+1,min(p[now]+now,n),now);
SEG.query_max(max(now-p[now],1),now-1,now);
}
printf("%d\n",dis[t]);
}
return 0;
}
J. Boundary
祁神刚开始大力观察了一波,猜测合法的答案一定满足中心对称性
因此手玩一下会发现其实有三种情况,\((w-1,l-1),(w-2,l),(w,l-2)\),对于每种情况,两边长的公约数显然都合法
然而写完交上去发现竟然WA了,随后徐神提出当\(a=2\)时存在不是中心对称图形的解,加上这个特判就过了
#include<bits/stdc++.h>
using namespace std;
int gcd(int a, int b){return 0==b ? a : gcd(b, a%b);}
int t;
set<int> st;
void solve(int x){
int i;
for (i=1; i*i<x; ++i){
if (x%i==0) st.insert(i), st.insert(x/i);
}
if (i*i==x) st.insert(i);
}
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> t;
while (t--){
int a, b;
cin >> a >> b;
st.clear();
solve(gcd(a, b-2));
solve(gcd(a-1, b-1));
solve(gcd(a-2, b));
st.insert(2);
cout << st.size() << ' ';
for (int x : st) cout << x << ' ';
cout << '\n';
}
return 0;
}
K. Gastronomic Event
很神的一个题,最后搞来搞去又变成经典的子树背包了,后面和徐神接力一起淦了过去
考虑钦定某个点为根,转化成有根树后怎么做
手玩一下会发现对于一个子树,假设其子树内的边的方向都相同一定是最优的
考虑怎么合并路径,对于某个点的若干子树,假设选择向上的子树size和为\(s_1\),选择向上的子树size和为\(s_2\),则路径条数的贡献为\((s_1+1)\times (s_2+1)\)
由于\(s_1+s_2\)的值恒定,根据均值不等式我们需要让\(s_1,s_2\)尽可能接近
这个问题很经典,最优的做法就是利用值域分类+二进制分组+bitset
优化,做到\(O(\sqrt n\log n\times \frac{n}{\omega})\)
不过这题中还有个好处就是当一个点不是重心时,我们没有必要跑这个背包,而是直接令size最大的子树单独一个方向即可,可以大大优化复杂度
最后再加上一个针对无根树的换根操作即可
#include <bits/stdc++.h>
using llsi = long long signed int;
llsi hl666(const std::vector<int> &v);
constexpr int $n = 1000005;
int n, fa[$n], siz[$n];
std::vector<int> ch[$n];
llsi dp[$n], ans = 0;
void dfs1(int now) {
// std::cerr << "now = " << now << char(10);
siz[now] = 1; dp[now] = 1;
for(auto ch: ch[now]) {
dfs1(ch);
siz[now] += siz[ch];
dp[now] += siz[ch] + dp[ch];
}
}
void dfs2(int now, llsi upd) {
llsi ms = n - siz[now];
for(auto ch: ch[now]) {
upd += dp[ch];
if(siz[ch] > ms) ms = siz[ch];
}
// std::cerr << "now, upd = " << now << ", " << upd << char(10);
if(ms > n / 2) upd += llsi(ms + 1) * (n - ms);
else {
std::vector<int> s; s.reserve(ch[now].size() + bool(now));
for(auto ch: ch[now]) s.push_back(siz[ch]);
if(now) s.push_back(n - siz[now]);
upd += hl666(s);
}
ans = std::max(ans, upd);
for(auto ch: ch[now]) {
llsi cache = dp[now];
dp[now] -= siz[ch] + dp[ch];
dp[ch] += (n - siz[ch]) + dp[now];
dfs2(ch, dp[now]);
dp[now] = cache;
}
}
int main(void) {
std::cin >> n;
for(int i = 1, a; i < n; ++i) {
std::cin >> a; fa[i] = --a;
ch[a].push_back(i);
}
dfs1(0);
dfs2(0, 0);
// for(int i = 0; i < n; ++i) std::cerr << dp[i] << char(i == n - 1 ? 10 : 32);
std::cout << ans << std::endl;
return 0;
}
llsi hl666(const std::vector<int> &v) {
std::bitset <1000005> f;
std::unordered_map <int,int> bkt;
int lim=0; for (auto x:v) lim+=x,++bkt[x];
f.set(0); for (auto [x,y]:bkt)
{
for (int k=1;y>=k;y-=k,k<<=1) f|=(f<<k*x);
if (y!=0) f|=(f<<y*x);
}
llsi ret=0; for (int i=0;i<=lim;++i)
if (f.test(i)) ret=std::max(ret,1LL*(i+1)*(lim-i+1));
return ret;
}
L. Circular Maze
祁神写的大力模拟题,由于角度都是整数,我们可以暴力模拟每个分隔出的“房间”,然后连通性用并查集处理即可
说起来简单但细节还是很多的,具体实现看代码
#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
int t, n;
int wall[22][365], room[22][365];
vector<int> hsh;
vector<pii> cir[22], stg[365];
vector<int> blk[22];
int fa[365*22];
int gf(int x){return x==fa[x] ? x : fa[x]=gf(fa[x]);}
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> t;
while (t--){
cin >> n;
hsh.clear();
for (int i=0; i<360; ++i) stg[i].clear();
for (int i=0; i<=20; ++i) cir[i].clear(), blk[i].clear();
for (int i=0; i<=20; ++i){
for (int j=0; j<360; ++j) wall[i][j]=room[i][j]=0;
}
for (int i=1; i<=n; ++i){
char ch; int a, b, c;
cin >> ch >> a >> b >> c;
// scanf("%c %d %d %d\n", &ch, &a, &b, &c);
// printf("i=%d %c %d %d %d\n", i, ch, a, b, c);
if ('C'==ch){
hsh.push_back(a);
cir[a].push_back(make_pair(b, c));
}else if ('S'==ch){
stg[c].push_back(make_pair(a, b));
}
}
hsh.push_back(0);
sort(hsh.begin(), hsh.end());
hsh.erase(unique(hsh.begin(), hsh.end()), hsh.end());
int szr = hsh.size();
// printf("szr=%d\n", szr);
// printf("hsh:"); for (int i=0; i<szr; ++i) printf("%d ", hsh[i]); puts("");
for (int i=0; i<360; ++i){
for (auto [l, r] : stg[i]){
// printf("i=%d [%d %d]\n", i, l, r);
for (int j=1; j<szr; ++j){
if (hsh[j]<=r && hsh[j-1]>=l) blk[j].push_back(i);
}
}
}
// printf("11111\n");
for (int i=1; i<=20; ++i) if (cir[i].size()>0){
int id = lower_bound(hsh.begin(), hsh.end(), i)-hsh.begin();
// printf("i=%d id=%d\n", i, id);
if (id>=szr) continue;
for (auto [l, r] : cir[i]){
if (l<r) for (int j=l; j<r; ++j) wall[id][j]=1;
else{
for (int j=l; j<360; ++j) wall[id][j]=1;
for (int j=0; j<r; ++j) wall[id][j]=1;
}
}
}
int idx=0;
for (int i=1; i<szr; ++i){
int sz = blk[i].size();
// printf("blk i=%d:", i); for (int x : blk[i]) printf("%d ", x); puts("");
if (sz<2){
++idx;
for (int j=0; j<360; ++j) room[i][j]=idx;
continue;
}
for (int j=0; j<sz-1; ++j){
++idx;
for (int k=blk[i][j]; k<blk[i][j+1]; ++k){
room[i][k] = idx;
}
}
++idx;
for (int k=blk[i][sz-1]; k<360; ++k) room[i][k]=idx;
for (int k=0; k<blk[i][0]; ++k) room[i][k]=idx;
}
++idx;
for (int k=0; k<360; ++k) room[szr][k]=idx;
for (int i=1; i<=idx; ++i) fa[i]=i;
for (int i=1; i<szr; ++i){
for (int j=0; j<360; ++j){
if (!wall[i][j] && gf(room[i][j])!=gf(room[i+1][j])) fa[gf(room[i][j])]=gf(room[i+1][j]);
}
}
// printf("hsh:"); for (int i=0; i<szr; ++i) printf("%d ", hsh[i]); puts("");
// for (int i=1; i<=szr; ++i){
// printf("i=%d r=%d\n", i, hsh[i]);
// for (int j=0; j<360; ++j) printf("j=%d (%d %d)\n", j, room[i][j], wall[i][j]); puts("");
// }
puts(gf(1)==gf(idx) ? "YES" : "NO");
}
return 0;
}
Postscript
感觉最近对线上训练的适应越来越好了,配合得越来越熟练了的说
标签:std,now,ch,Contest,int,2021,2022,include,define From: https://www.cnblogs.com/cjjsb/p/17993219