T1 string
一道构造题。大概是这样的,首先 k 等于 1 的时候,如果 1 在第 i 个位置那么答案就是 i ( n − i + 1 ) i(n-i+1) i(n−i+1),如果 k 等于 2,两个 1 分别在 i j然后 i < j 。那么答案就是 ( − i + j ) ( n + i − j + 1 ) (-i+j)(n+i-j+1) (−i+j)(n+i−j+1),如果 k 等于 3,三个 1 分别在 i, j, k 且 i < j < k,那么答案就是 ( i − j + k ) ( n − i + j − k + 1 ) (i-j+k)(n-i+j-k+1) (i−j+k)(n−i+j−k+1),…
然后就找到规律就是说令 a 为 1 的下标:
T
=
(
−
1
)
k
∑
i
=
1
k
(
−
1
)
i
a
i
T = (-1)^k\sum_{i=1}^k(-1)^ia_i
T=(−1)ki=1∑k(−1)iai
答案就是:
a
n
s
=
T
(
n
−
T
+
1
)
=
−
T
2
+
(
n
+
1
)
T
ans = T(n - T + 1) = -T^2 + (n+1)T
ans=T(n−T+1)=−T2+(n+1)T
然后我们就可以把这玩意儿看成一个关于 T 的二次函数,求出最大值:
a
n
s
=
−
(
T
−
n
+
1
2
)
2
+
(
n
+
1
)
2
4
→
a
n
s
m
a
x
=
(
n
+
1
)
2
4
ans = -(T-\frac{n+1}{2})^2 + \frac{(n+1)^2}{4} \rightarrow ans_{max} = \frac{(n+1)^2}{4}
ans=−(T−2n+1)2+4(n+1)2→ansmax=4(n+1)2
然后就要构造一个串让这个串的 T 等于 n + 1 2 \frac{n+1}{2} 2n+1 就好了。一种简单的构造方法就是先在最前面连续放 k − 1 k - 1 k−1 个 1,然后再在后面找一个位置让它们算出来是 T 就好了。最后还要特判一下 k = 0 的情况。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100100
#define endl '\n'
int ans[MAXN] = { 0 };
int n = 0; int k = 0;
int main(){
scanf("%d%d", &n, &k);
if(k == 0){
puts("0");
for(int i = 1; i <= n; i++) cout << 0 << ' ';
puts("");
return 0;
}
cout << 1ll * (n + 1) * (n + 1) / 4 << endl;
for(int i = 1; i < k; i++) ans[i] = 1;
ans[(n + k + 1 - (k & 1)) / 2] = 1;
for(int i = 1; i <= n; i++) cout << ans[i] << ' ';
puts("");
return 0;
}
T2 artist
一道线段树的题,在考场上打了一个模拟 O ( n 3 ) O(n^3) O(n3) 然后得了 10 分qwq。
#include<bits/stdc++.h>
using namespace std;
#define in read()
#define MAXN 500500
#define endl '\n'
inline int read(){
int x = 0; char c = getchar();
while(c < '0' or c > '9') c = getchar();
while('0' <= c and c <= '9'){
x = x * 10 + c - '0'; c = getchar();
}
return x;
}
struct Trange{
int l; int r;
};
int q = 0;
int n = 0; int m = 0;
int c[MAXN] = { 0 };
int l[MAXN] = { 0 };
Trange r[MAXN] = { 0 };
bool check(int idx){
int tong[MAXN] = { 0 };
for(int i = r[idx].l; i <= r[idx].r; i++)
if(!tong[c[i]]) tong[c[i]]++;
else return false;
return true;
}
int main(){
freopen("artist.in", "r", stdin);
freopen("artist.out", "w", stdout);
n = in; m = in; q = in;
for(int i = 1; i <= n; i++) c[i] = in;
for(int i = 1; i <= m; i++){
r[i].l = in; r[i].r = in;
}
for(int i = 1; i <= m; i++) l[i] = -1;
for(int i = 1; i <= m; i++) if(check(i)) l[i] = 0;
for(int i = 1; i <= q; i++){
int x = in; int y = in;
c[x] = y;
for(int j = 1; j <= m; j++)
if(check(j) and l[j] == -1) l[j] = i;
}
for(int i = 1; i <= m; i++) if(l[i] == -1) l[i] = m + i;
int ans = 0;
for(int i = 1; i <= m; i++) ans ^= l[i];
cout << ans << endl;
return 0;
}
下面是正解:
因为每个区间要么相互包含要么不想交,那我们可以对所有区间建一棵树,范围大的区间为父节点,范围小的区间为子节点。然后每一个节点内就存储对应区间内的颜色信息。因为只有在儿子被处理成全都不一样之后父亲才可能被处理成全都不一样。所以每次修改我们只修改儿子。等到儿子符合条件,再暴力将儿子的部分全部复制给父亲。
对于询问来说,我们可以对每个区间维护一个 last 数组,last[i] 表示 i 之前的第一个与 i 相同颜色的下标。这样一个区间内颜色互不相同的个数就等价于 ( r − l + 1 ) − max i = l r { l a s t i } (r-l+1) - \max\limits_{i=l}^r \lbrace last_i\rbrace (r−l+1)−i=lmaxr{lasti},这玩意儿就可以用一个 set 和线段树来维护。
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define in read()
#define MAXN 500500
typedef pair<int, int> pii;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' or c > '9') c = getchar();
while('0' <= c and c <= '9'){
x = x * 10 + c - '0'; c = getchar();
}
return x;
}
int q = 0;
int n = 0; int m = 0;
int pre[MAXN] = { 0 };
int fa[MAXN] = { 0 };
int c[MAXN] = { 0 };
int d[MAXN] = { 0 };
int l[MAXN] = { 0 };
pii b[MAXN];
set<int> co[MAXN];
set<pair<pii, int> > s;
struct Tnode{
int l, r;
int dat;
}t[4 * MAXN];
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)
inline void pushup(int p){
t[p].dat = max(t[ls(p)].dat, t[rs(p)].dat);
}
void build(int p, int l, int r){
t[p].l = l; t[p].r = r;
if(l == r){
t[p].dat = pre[l];
return;
}
int mid = (l + r) >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
pushup(p);
}
void update(int p, int index, int val){
if(t[p].l == t[p].r){
t[p].dat = val;
return;
}
int mid = (t[p].l + t[p].r) >> 1;
if(index <= mid) update(ls(p), index, val);
else update(rs(p), index, val);
pushup(p);
}
int query(int p, int l, int r){
if(l <= t[p].l and t[p].r <= r) return t[p].dat;
int mid = (t[p].l + t[p].r) >> 1; int ans = -1;
if(l <= mid) ans = max(ans, query(ls(p), l, r));
if(r > mid) ans = max(ans, query(rs(p), l, r));
return ans;
}
inline bool isin(pii x, pii y){
return y.fi <= x.fi and x.se <= y.se;
}
inline bool comp(pair<pii, int> x, pair<pii, int> y){
return x.fi.fi == y.fi.fi ? x.fi.se > y.fi.se : x.fi.fi < y.fi.fi;
}
void init(){
for(int i = 1; i <= n; i++) co[i].insert(0);
for(int i = 1; i <= n; i++){
pre[i] = *(co[c[i]].rbegin());
co[c[i]].insert(i);
}
build(1, 1, n);
pair<pii, int> temp[MAXN];
b[0].fi = -1; b[0].se = n + 1;
for(int i = 0; i <= m; i++){
temp[i].fi = b[i]; temp[i].se = i;
}
sort(temp, temp + m + 1, comp);
stack<int> stk; stk.push(0);
for(int i = 1; i <= m; i++){
while(!isin(temp[i].fi, temp[stk.top()].fi)) stk.pop();
fa[temp[i].se] = temp[stk.top()].se; d[fa[temp[i].se]]++;
stk.push(i);
}
for(int i = 1; i <= m; i++){
if(!d[i]) s.insert({b[i], i});
}
}
void modify(int x, int d){
auto p = co[c[x]].lower_bound(x);
auto bf = p; auto nx = p; nx++; bf--;
if(nx != co[c[x]].end()) update(1, *nx, *bf);
co[c[x]].erase(x);
c[x] = d;
nx = co[c[x]].lower_bound(x);
bf = nx; bf--; update(1, x, *bf);
if(nx != co[c[x]].end()) update(1, *nx, x);
co[c[x]].insert(x);
}
int queryB(int x){
auto p = s.upper_bound({{x, n + 1}, m + 1});
if(p == s.begin()) return -1;
p--; auto B = *p;
if(B.fi.fi <= x and x <= B.fi.se) return B.se;
return -1;
}
void check(int id, int i){
if(id <= 0) return;
if(query(1, b[id].fi, b[id].se) >= b[id].fi) return;
l[id] = i; s.erase({b[id], id});
if(!(--d[fa[id]])){
s.insert({b[fa[id]], fa[id]}); check(fa[id], i);
}
}
int main(){
n = in; m = in; q = in;
for(int i = 1; i <= n; i++) c[i] = in;
for(int i = 1; i <= m; i++){
b[i].fi = in; b[i].se = in; l[i] = -1;
}
init();
vector<int> leaf;
for(auto v : s) leaf.push_back(v.se);
for(int v : leaf) check(v, 0);
for(int i = 1; i <= q; i++){
int x = in; int y = in;
modify(x, y); check(queryB(x), i);
}
int ans = 0;
for(int i = 1; i <= m; i++){
if(l[i] == -1) ans ^= m + i;
else ans ^= l[i];
}
cout << ans << endl;
return 0;
}