总结
本场前三题较为简单。
D题涉及到了推公式+二分
E题dfs序+线段树
可以发现这场题目数据范围提示比较明显。 多多注意数据范围!!
A. Professor GukiZ’s Robot
int main()
{
// int T;
// scanf("%d", &T);
// while(T --)
// {
ll x1, y1, x2, y2;
scanf("%lld %lld %lld %lld", &x1, &y1, &x2, &y2);
ll temp1 = abs(x1 - x2), temp2 = abs(y1 - y2);
printf("%lld\n", min(temp1, temp2) + temp1 - min(temp1, temp2) + temp2 - min(temp1, temp2));
// }
return 0;
}
B. Grandfather Dovlet’s calculator
int a[] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
ll cal(int now)
{
ll sum = 0;
while(now)
{
int dig = now % 10;
sum += a[dig];
now /= 10;
}
return sum;
}
int main()
{
ll a ,b;
scanf("%lld %lld", &a, &b);
ll res = 0;
for(int i = a ; i <= b ; i ++)
{
res += cal(i);
}
printf("%lld\n",res);
return 0;
}
C. Pearls in a Row
ll a[maxn];
map <ll, int> mp;
int main()
{
int n;
scanf("%d", &n);
for(int i = 1 ; i <= n ; i ++)
scanf("%lld", &a[i]);
vector <PII> alls;
int l = -1, r;
for(int i = 1 ; i <= n ; i ++)
{
mp[a[i]] ++;
if(l == -1)
l = i;
else if(mp[a[i]] == 2)
{
r = i;
alls.push_back({l, r});
for(int j = l ; j <= r ; j ++)
mp[a[j]] --;
l = -1;
}
}
if(alls.empty())
printf("-1\n");
else
{
printf("%d\n", alls.size());
if(alls[alls.size() - 1].second != n)
alls[alls.size() - 1].second = n;
for(auto t : alls)
printf("%d %d\n", t.first, t.second);
}
return 0;
}
D. Professor GukiZ and Two Arrays
题意:
给定两个数组A,B,问最多经过两次交换AB数组中任意元素,使得的差值要尽量小并输出具体方案。
思路:
一开始没有想到分类讨论。感觉是DP。
但是DP了半天,没有de出来。
赛后发现是分类讨论。想想的确也对。毕竟要求最多经过两次交换。!!!!
数据范围提示非常明显啊!!!!
K = 0时, ans = abs(suma - sumb)
K = 1时, ans = abs(suma - sumb - 2 * (a[i] - b[j])) (可预处理出来, 毕竟n <= 2e3)
K = 2时, ans = abs(suma - sumb - 2 * (a[i] - b[j]) - 2 * (a[k] - b[t]))
那么可以发现只剩下最后一种情况。
可以发现当枚举k、t时。abs(suma - sumb - 2 * (a[k] - b[t]))为定值,设为s。
只有2 * (a[i] - b[j])不定。
此外可以发现i、j、k、t各不相同。
考虑二分,可以二分出 2 * (a[i] - b[j]) <= s的最后一个数。
二分出2 * (a[i] - b[j]) >= s的第一个数。
假设二分到的值,已经为k、t重复了。那么直接对应往后/往前移动就好了
struct note{ //
int l;
int r;
ll w;
}A[maxn];
ll a[maxn], b[maxn];
bool cmp(note A, note B)
{
return A.w < B.w;
}
int main()
{
int n;
scanf("%d", &n);
ll suma = 0, sumb = 0;
for(int i = 1 ; i <= n ; i ++)
scanf("%lld", &a[i]), suma += a[i];
int m;
scanf("%d", &m);
for(int i = 1 ; i <= m ; i ++)
scanf("%lld", &b[i]), sumb += b[i];
vector <PII> ans;
ll res = abs(suma - sumb);
int cnt = 0;
for(int i = 1 ; i <= n ; i ++)
{
for(int j = 1 ; j <= m ; j ++)
{
A[++ cnt] = {i, j, 2ll * (a[i] - b[j])};
if(res > abs(suma - sumb - 2ll * (a[i] - b[j])))
{
vector <PII> temp;
res = abs(suma - sumb - 2ll * (a[i] - b[j]));
temp.push_back({i, j});
ans = temp;
}
}
}
sort(A + 1, A + m * n + 1, cmp);
for(int k = 1 ; k <= n ; k ++)
{
for(int t = 1 ; t <= m ; t ++)
{
ll temp = suma - sumb - 2ll * (a[k] - b[t]);
int l = 1, r = m * n;
while(l < r)
{
int mid = (l + r) >> 1;
if(A[mid].w >= temp) r = mid;
else l = mid + 1;
}
if(A[l].w >= temp)
{
while(A[l].l == k || A[l].r == t)
{
l ++;
}
if(l <= m * n)
{
if(res > abs(temp - A[l].w))
{
res = abs(temp - A[l].w);
vector <PII> c;
c.push_back({A[l].l, A[l].r});
c.push_back({k, t});
ans = c;
}
}
}
l = 1, r = m * n;
while(l < r)
{
int mid = (l + r + 1) >> 1;
if(A[mid].w <= temp) l = mid;
else r = mid - 1;
}
if(A[l].w <= temp)
{
while(A[l].l == k || A[l].r == t)
{
l --;
}
if(l >= 1)
{
if(res > abs(temp - A[l].w))
{
res = abs(temp - A[l].w);
vector <PII> c;
c.push_back({A[l].l, A[l].r});
c.push_back({k, t});
ans = c;
}
}
}
}
}
printf("%lld\n",res);
printf("%d\n", ans.size());
for(auto t : ans)
printf("%d %d\n", t.first, t.second);
return 0;
}
E. New Year Tree
题意:
给定n个顶点,n - 1条边。每个顶点都有一个颜色。
m个查询。
query1:v c :把v所在的子树中的点全部染色为颜色c
query2: v : 查询此时v所在的子树中颜色的种类数
思路:
首先发现存在修改操纵。不能用近段时间学的Dsu on tree 且 颜色数量 <= 60。
存在区间修改、区间查询。
由于颜色数量少,直接开longlong用二进制表示即可(区间合并用 | 运算符即可)。
code:
ll c[maxn];
int h[maxn], ne[maxn * 2], e[maxn * 2], idx, in[maxn], out[maxn], tim, id[maxn];
void add(int u, int v)
{
e[idx] = v;
ne[idx] = h[u];
h[u] = idx ++;
}
void dfs(int sta, int fa)
{
in[sta] = ++ tim;
id[tim] = sta;
for(int i = h[sta] ; i != -1 ; i = ne[i])
{
int son = e[i];
if(son == fa) continue;
dfs(son, sta);
}
out[sta] = tim;
}
struct note{
int l;
int r;
ll lazy;
ll w;
int mid()
{
return (l + r) >> 1;
}
}tre[maxn << 2];
void pushup(int rt)
{
tre[rt].w = tre[rt * 2].w | tre[rt * 2 + 1].w;
}
void pushdown(int rt)
{
if(tre[rt].lazy > 0)
{
tre[rt * 2].w = 1ll << tre[rt].lazy;
tre[rt * 2 + 1].w = 1ll << tre[rt].lazy;
tre[rt * 2].lazy = tre[rt].lazy;
tre[rt * 2 + 1].lazy = tre[rt].lazy;
tre[rt].lazy = 0;
}
}
void build(int rt, int l, int r)
{
tre[rt] = {l, r, 0ll};
if(tre[rt].l == tre[rt].r) //叶子
{
tre[rt].w = 1ll << c[id[tre[rt].l]];
return ;
}
int mid = tre[rt].mid();
build(rt * 2, l, mid), build(rt * 2 + 1, mid + 1, r);
pushup(rt);
}
void modify(int rt, int l, int r, ll t) //区间修改
{
if(tre[rt].l == l && tre[rt].r == r)
{
tre[rt].w = 1ll << t;
tre[rt].lazy = t;
// printf("ffff %d %lld %lld\n", id[tre[rt].l], t, tre[rt].w);
return ;
}
pushdown(rt);
int mid = tre[rt].mid();
if(l > mid) //右子树
modify(rt * 2 + 1, l, r, t);
else if(r <= mid)
modify(rt * 2, l, r, t);
else
{
modify(rt * 2, l, mid, t);
modify(rt * 2 + 1, mid + 1, r, t);
}
pushup(rt);
}
note query(int rt, int l, int r)
{
if(tre[rt].l == l && tre[rt].r == r)
{
return tre[rt];
}
pushdown(rt);
int mid = tre[rt].mid();
if(r <= mid)
return query(rt * 2, l, r);
else if(l > mid)
return query(rt * 2 + 1, l, r);
else
{
note temp, temp1, temp2;
temp1 = query(rt * 2, l, mid), temp2 = query(rt * 2 + 1, mid + 1 , r);
temp.w = temp1.w | temp2.w;
return temp;
}
}
int main()//区间修改
{
int n, m;
scanf("%d %d", &n, &m);
for(int i = 1 ; i <= n ; i ++)
scanf("%lld", &c[i]);
memset(h, -1 , sizeof(h)), idx = 0;
for(int i = 1 ; i < n ; i ++)
{
int u, v;
scanf("%d %d", &u, &v);
add(u, v), add(v, u);
}
dfs(1, -1);
build(1, 1, n);
while(m --)
{
int t;
scanf("%d", &t);
if(t == 1)
{
int v;
ll d;
scanf("%d %lld", &v, &d);
modify(1, in[v], out[v], d);
// printf("f : %lld\n", query(1, in[v], out[v]).w);
}
else
{
int v;
scanf("%d", &v);
ll temp = query(1, in[v], out[v]).w;
int cnt = 0;
for(ll i = 62 ; i >= 0 ; i --)
{
if(temp >> i & 1)
cnt ++;
}
printf("%d\n", cnt);
}
}
return 0;
}