\(100+100+30+45=275\)
开场 \(10\) 分钟,开始码 A,码了一个小时无果删码跑路。
十几分钟把 B 切了开始单挑 C,失败。
写完 D 暴力后剩 30min,极限写完 A。
A: 小 H 的积木
把最上方和最下方的都加入线段树,如果有一个数加入了 \(2\) 次,就把它放入待选答案里面,每次选择最小的同时放入与他相邻的数。
点击查看代码
#include <bits/stdc++.h>
#define fr first
#define se second
#define Un unsigned
#define LL long long
#define pb push_back
#define pii pair<int,int>
#define pLi pair<LL,int>
#define pLL pair<LL,LL>
#define __ __int128
#define LD long double
#define VE vector<LL>
#define DB double
#define Ve vector<int>
using namespace std;
inline LL read()
{
LL x = 0,f = 1;char ch = getchar();
while(!isdigit(ch)) (ch == '-') && (f = -1),ch = getchar();
while(isdigit(ch)) x = x*10+ch-48,ch = getchar();
return x*f;
}
const int N = 2e5+5,INF = 2e9;
vector < pii > pos[N];
Ve a[N],L,R;
unordered_map < int,bool > hd[N],tl[N],in[N];
bool vis[N];
struct tree{int l,r,cnt;pii mn;}tr[N<<2];
void build(int l,int r,int i)
{
tr[i] = {l,r,0,{INF,0}};
if (l == r) return ;
int mid = (l+r)>>1;
build(l,mid,i<<1),build(mid+1,r,i<<1|1);
}
void M(int p,int k,int i)
{
if (tr[i].l == tr[i].r)
{
tr[i].cnt += k;
if (tr[i].cnt >= 2)
{
auto [a,b] = pos[tr[i].l][0];
auto [c,d] = pos[tr[i].l][1];
if ((hd[a][b] && tl[c][d]) || (tl[a][b] && hd[c][d]))
{
if (hd[a][b])
{
if (tl[c][d]) tr[i].mn = {a,tr[i].l};
else tr[i].mn = {c,tr[i].l};
}
else tr[i].mn = {c,tr[i].l};
}
else tr[i].mn = {INF,0};
}
else tr[i].mn = {INF,0};
return ;
}
if (p <= tr[i<<1].r) M(p,k,i<<1);
else M(p,k,i<<1|1);
tr[i].mn = min(tr[i<<1].mn,tr[i<<1|1].mn);
}
int main()
{
int n = read(),m = read();
for (int i = 1,k;i <= m;i++)
{
k = read();
for (int j = 0,x;j < k;j++) x = read(),a[i].pb(x),pos[x].pb({i,j});
}
for (int i = 1;i <= n;i++)
{
if (pos[i].size() != 2) return puts("-1"),0;
sort(pos[i].begin(),pos[i].end());
}
build(1,n,1);
for (int i = 1;i <= m;i++)
{
hd[i][0] = 1,tl[i][a[i].size()-1] = 1,M(a[i][0],1,1);
if (a[i].size() > 1)
M(a[i].back(),1,1);
}
for (int i = 1;i <= n;i++)
{
auto [x,y] = tr[1].mn;
if (x == INF) return puts("-1"),0;
auto [u,v] = pos[y][0];
auto [e,f] = pos[y][1];
M(y,-1e9,1),vis[y] = 1;
L.pb(x);
if (x == u)
{
R.pb(e);
if (v+1 < a[u].size() && !vis[a[u][v+1]]) hd[u][v+1] = 1,M(a[u][v+1],1,1);
if (f-1 >= 0 && !vis[a[e][f-1]])
{
tl[e][f-1] = 1,M(a[e][f-1],0,1);
if (!hd[e][f-1]) M(a[e][f-1],1,1);
}
}
else
{
R.pb(u);
if (f+1 < a[e].size() && !vis[a[e][f+1]])
hd[e][f+1] = 1,M(a[e][f+1],1,1);
if (v-1 >= 0 && !vis[a[u][v-1]])
{
tl[u][v-1] = 1,M(a[u][v-1],0,1);
if (!hd[u][v-1]) M(a[u][v-1],1,1);
}
}
}
for (int i : L) printf("%d ",i);
reverse(R.begin(),R.end());
for (int i : R) printf("%d ",i);
return 0;
}
B: 小 H 的树
有两种方式,一种是断树边,另一种是把环断开,找出来环断环为链双指针搞一下即可。
点击查看代码
#include <bits/stdc++.h>
#define fr first
#define se second
#define Un unsigned
#define LL long long
#define pb push_back
#define pii pair<int,int>
#define pLi pair<LL,int>
#define pLL pair<LL,LL>
#define __ __int128
#define LD long double
#define VE vector<LL>
#define DB double
#define Ve vector<int>
using namespace std;
inline LL read()
{
LL x = 0,f = 1;char ch = getchar();
while(!isdigit(ch)) (ch == '-') && (f = -1),ch = getchar();
while(isdigit(ch)) x = x*10+ch-48,ch = getchar();
return x*f;
}
const int N = 2e5+5;
struct edge{int to,pre;}e[N<<1];
int a[N],las[N],cnt,pre[N],r[N<<1],ct;
bool in[N],flg,vis[N];
LL sum,s[N],ans = 1e18;
void add(int u,int v){e[++cnt] = {v,las[u]},las[u] = cnt;}
void D(int x)
{
if (flg) return ;
vis[x] = 1;
for (int i = las[x],y;i && !flg;i = e[i].pre)
{
if ((y = e[i].to) != pre[x])
{
if (vis[y])
{
r[++ct] = y,in[y] = 1,flg = 1;
while(x != y) r[++ct] = x,in[x] = 1,x = pre[x];
}
else pre[y] = x,D(y);
}
}
}
void D1(int x,int fa)
{
s[x] = a[x];
for (int i = las[x],y;i;i = e[i].pre)
if ((y = e[i].to) != fa && !in[y]) D1(y,x),s[x] += s[y];
if(!in[x]) ans = min(ans,llabs(sum-2*s[x]));
}
int main()
{
int n = read();
for (int i = 1;i <= n;i++) sum += (a[i] = read());
for (int i = 1,u,v;i <= n;i++)
u = read(),v = read(),add(u,v),add(v,u);
D(1);
for (int i = 1;i <= ct;i++) D1(r[i],0),r[i+ct] = r[i];
LL res = 0;int j = 1;
for (int i = 1;i <= ct;i++)
{
while(j < i+ct && res < sum/2) res += s[r[j++]];
ans = min(ans,llabs(sum-2*res));
res -= s[r[i]];
}
cout << ans;
return 0;
}****
C: 小 H 的串
发现一种答案可能对应多种操作,赛时试图使用脑袋中仅剩的 \(\text{Trick}\) :把答案和操作间形成单射,对应同一答案的操作序列只在字典序最小时统计。但是根本进行不下去。
于是放弃对操作进行计数,我们对合法答案计数即可。
由于 \(t\) 很短,所以考虑状压。
设 \(f_{i, sta}\) 表示结果串长 \(i\),匹配状态为 \(sta\) 的结果串个数。
匹配状态 \(sta\):若 \(sta\) 第 \(j\) 为 \(1\),那么 存在一种用 \(s1_{1\ldots i - j}\) 和 \(s2_{1\ldots j}\) 拼出结果串的操作序列。
转移时枚举 \(i + 1\) 位填什么字符求下一个状态转移。
解析:
D: 小 H 的蛋糕F. Array Covering
wqs 二分大法好。
标签:ch,int,11.20,tr,&&,hd,define From: https://www.cnblogs.com/ZepX-D/p/18559499