2022NOIPA层联测2
二分图排列
不想写了
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 1000005;
int n, q, a[maxn];
int f[maxn][2], tmp[4][2];
void solve(){
for(int i = 1; i <= n; ++i)f[i][0] = f[i][1] = -maxn;
f[n][0] = f[n][1] = maxn;
for(int i = n; i > 1; --i){
tmp[0][0] = -a[i]; tmp[0][1] = f[i][0];
tmp[1][0] = a[i]; tmp[1][1] = f[i][1];
tmp[2][0] = f[i][0]; tmp[2][1] = -a[i];
tmp[3][0] = f[i][1]; tmp[3][1] = a[i];
for(int j = 0; j <= 1; ++j){
int now = j ? a[i - 1] : -a[i - 1];
for(int k = 0; k <= 3; ++k)if(now < tmp[k][0])f[i - 1][j] = max(f[i - 1][j], tmp[k][1]);
}
if(f[i - 1][0] == f[i - 1][1] && f[i - 1][0] == -maxn){
printf("NO\n");
return;
}
}
printf("YES\n");
int p1 = -maxn, p2 = -maxn;
for(int i = 1; i <= n; ++i){
if(-a[i] > p1)p1 = a[i] = -a[i];
else if(-a[i] > p2 && -a[i] < f[i][0])p2 = a[i] = -a[i];
else p1 = a[i];
if(p1 < p2)swap(p1, p2);
}
for(int i = 1; i <= n; ++i)printf("%d ",a[i]);
printf("\n");
}
int main(){
q = read();
for(int ask = 1; ask <= q; ++ask){
n = read();
for(int i = 1; i <= n; ++i)a[i] = read();
solve();
}
return 0;
}
最短路问题 V3
水,但是人傻,给 \(q\) 带上了 \(44log(44 * 43 / 2)\) 的常数
在 \(TLEcoders\) 上你不 \(T\) 谁 \(T\)
建出树,非树边建新图,特殊点最多 \(42\) 个,于是每次处理询问点和他们之间的最短路即可
但是其实可以更加优化常数
从特殊点跑到所有点最短路,然后枚举经过了哪个特殊点,或者树上路径即可
code
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 100005;
typedef pair<int,ll> pil;
vector<pil>g[maxn];
struct edge{int to, net, val;}e[maxn << 1 | 1];
int head[maxn], tot;
void add(int u, int v, int w){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
e[tot].val = w;
}
int n, m, q;
int f[maxn];
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
int t[maxn], cnt;
ll sum[maxn];
int dep[maxn], size[maxn], son[maxn], top[maxn];
void dfs1(int x){
size[x] = 1;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == f[x])continue;
f[v] = x; dep[v] = dep[x] + 1; sum[v] = sum[x] + e[i].val;
dfs1(v);
size[x] += size[v];
son[x] = size[son[x]] > size[v] ? son[x] : v;
}
}
void dfs2(int x, int tp){
top[x] = tp;
if(son[x])dfs2(son[x], tp);
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == f[x] || v == son[x])continue;
dfs2(v, v);
}
}
int lca(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]])swap(u, v);
u = f[top[u]];
}
if(dep[u] < dep[v])return u;
return v;
}
ll di[maxn];
bool vis[maxn], ts[maxn];
typedef pair<ll, int> pli;
priority_queue <pli, vector<pli>, greater<pli> >Q;
void solve(){
int u = read(), v = read();
if(!ts[u])
for(int i = 1; i <= cnt; ++i){
int lc = lca(u, t[i]);
ll dis = sum[u] + sum[t[i]] - sum[lc] - sum[lc];
g[u].push_back(pil(t[i], dis));
g[t[i]].push_back(pil(u, dis));
}
if(!ts[v])
for(int i = 1; i <= cnt; ++i){
int lc = lca(v, t[i]);
ll dis = sum[v] + sum[t[i]] - sum[lc] - sum[lc];
g[v].push_back(pil(t[i], dis));
g[t[i]].push_back(pil(v, dis));
}
di[u] = 0; Q.push(pli{0, u});
while(!Q.empty()){
int x = Q.top().second; Q.pop();
if(vis[x])continue;
vis[x] = 1;
for(pil now : g[x]){
if(di[now.first] > di[x] + now.second){
di[now.first] = di[x] + now.second;
Q.push(pli(di[now.first], now.first));
}
}
}
int lc = lca(u, v);
ll ans = min(di[v], sum[u] + sum[v] - sum[lc] - sum[lc]);
printf("%lld\n",ans);
for(int i = 1; i <= cnt; ++i)di[t[i]] = di[0], vis[t[i]] = 0;
di[u] = di[v] = di[0]; vis[u] = vis[v] = 0;
if(!ts[u])
for(int i = 1; i <= cnt; ++i){
g[u].pop_back();
g[t[i]].pop_back();
}
if(!ts[v])
for(int i = 1; i <= cnt; ++i){
g[v].pop_back();
g[t[i]].pop_back();
}
}
signed main(){
n = read(), m = read();
for(int i = 1; i <= n; ++i)f[i] = i;
for(int i = 1; i<= m; ++i){
int u = read(), v = read(), w = read();
if(fa(u) == fa(v)){
t[++cnt] = u; t[++cnt] = v;
ts[u] = ts[v] = 1;
g[u].push_back(pil(v, w));
g[v].push_back(pil(u, w));
}else{
add(u, v, w); add(v, u, w);
f[fa(u)] = fa(v);
}
}
sort(t + 1, t + cnt + 1); cnt = unique(t + 1, t + cnt + 1) - t - 1;
for(int i = 1; i <= n; ++i)f[i] = 0;
dfs1(1); dfs2(1, 1);
for(int i = 1; i <= cnt; ++i)
for(int j = i + 1; j <= cnt; ++j){
int lc = lca(t[i], t[j]);
ll dis = sum[t[i]] + sum[t[j]] - sum[lc] - sum[lc];
g[t[i]].push_back(pil(t[j], dis));
g[t[j]].push_back(pil(t[i], dis));
}
memset(di, 0x3f, sizeof(di));
q = read();
for(int i = 1; i <= q; ++i)solve();
return 0;
}
捡石子游戏
考虑 \(n = 2\) 发现只有放在 \(w\) 大于旁边的才会胜利
扩展下去,我们只会沿着一条递增的路径走,当周围没有比他大的点时,该位置先手必败
于是对每个起始点 \(DFS\) 下去看递增链长度即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 3005;
int n, w[maxn], head[maxn], tot;
struct edge{int to, net;}e[maxn << 1 | 1];
void add(int u, int v){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
}
bool dfs(int x, int fa){
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa)continue;
if(w[x] > w[v] && dfs(v, x) == false)return true;
}
return false;
}
int main(){
n = read();
for(int i = 1; i <= n; ++i)w[i] = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
add(u, v); add(v, u);
}
bool fl = 0;
for(int i = 1; i <= n; ++i)if(dfs(i, 0)){
printf("%d ", i);
fl = 1;
}
if(!fl)printf("-1\n");
return 0;
}``cpp
凹函数
如果你想拟合个什么函数,就不要想了
转化成选择斜率不同的坐标都为正数,且 \(gcd(x, y) == 1\) 的向量
至于函数是啥,不重要
进一步转化发现把恰好 \(n, m\) 变成至多 \(n, m\) 没有影响
于是就有了朴素的背包 \(dp\)
一个比较经典的套路,发现答案范围小于坐标范围,于是交换下标和维护值,这个背包显然能够做到
于是,我们再加入一个剪枝,选择一个 \((x, y)\) 之前 \((a, b) a <= x, b <= y\) 一定被选
于是对满足 \(a <= x , b <= y\) 的向量求和,如果 \(>= n\) 那么该向量不能选择
于是通过一些证明得到此时向量的数量为 \(n^{2/ 3}\) 于是可以通过此题
证明不会
code
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 10005;
const int inf = 0x3f3f3f3f;
int f[4005][405], size[4005], sum[4005][2], mx;
void modify(int x, int y){
for(int i = mx; i >= x; --i){
for(int j = 0; j <= size[i - x]; ++j){
int now = f[i - x][j] + y;
if(now > mx)break;
f[i][j + 1] = min(f[i][j + 1], now);
}
if(f[i][size[i] + 1] < inf)f[i][++size[i] + 1] = inf;
}
}
int solve(int n, int m){return upper_bound(f[n], f[n] + size[n] + 1, m) - f[n];}
void pre(){
for(int i = 0; i <= mx; ++i)f[i][1] = inf;
for(int i = 1; i <= mx; ++i){
int sx = 0, sy = 0;
for(int j = 1; j <= mx && max(sum[j][0], sum[j][1]) <= mx; ++j){
if(__gcd(i, j) == 1){
sx += i; sy += j;
if(max(sum[j][0] + sx, sum[j][1] + sy) <= mx)modify(i, j);
}
sum[j][0] += sx;
sum[j][1] += sy;
}
}
}
int n[maxn], m[maxn];
int main(){
int q = read();
for(int i = 1; i <= q; ++i){
n[i] = read(), m[i] = read();
mx = max(max(n[i], m[i]), mx);
}
pre();
for(int i = 1; i <= q; ++i)printf("%d\n",solve(n[i], m[i]));
return 0;
}