Problem D. DRX vs. T1
题意:问谁能赢这个bo5
code
void solve(){
string s;
cin >> s;
int t, d, m;
t = d = m = 0;
for(auto i : s)
if(i == 'T')
t ++;
else if(i == 'D') d ++;
else m ++;
if(t == 3)
cout << "T1" << endl;
else cout << "DRX" << endl;
}
Problem C. Clamped Sequence
题意:设定一个区间,求
∑
a
b
s
(
a
[
i
]
−
a
[
i
+
1
]
)
\sum{abs(a[i]-a[i+1])}
∑abs(a[i]−a[i+1])
思维:以每个
a
i
a_i
ai 分别为上下界,求max就行
code
#define rep(a,b,c) for(int a=b;a<=c;a++)
const int N = 1e6 + 10;
int a[N], b[N];
int n, m;
void solve(){
cin >> n >> m;
rep(i,1,n)
cin >> a[i];
int ans = 0;
rep(i,1,n){
int res = 0;
int l = a[i] - m, r = a[i];
rep(j,1,n){
if(a[j] < l)
b[j] = l;
else if(a[j] > r)
b[j] = r;
else b[j] = a[j];
}
rep(i,2,n)
res += abs(b[i] - b[i - 1]);
ans = max(ans, res);
res = 0;
l = a[i], r = a[i] + m;
rep(j,1,n){
if(a[j] < l)
b[j] = l;
else if(a[j] > r)
b[j] = r;
else b[j] = a[j];
}
rep(i,2,n)
res += abs(b[i] - b[i - 1]);
ans = max(ans, res);
}
cout << ans << endl;
}
Problem L. Tavern Chess
题意:(读题喵,读不懂题就似了喵)
思路:模拟,就dfs暴力模拟这个过程就完了
code
const int N = 10;
struct Node
{
int x, y;
int sum = 0;
}a[N], b[N];
int n, m;
long double q, w, e;
bool check(long double res){
int st1, st2;
st1 = st2 = 0;
rep(i,0,n - 1){
if(a[i].y > 0){
st1 = 1;
}
}
rep(i,0,m - 1){
if(b[i].y > 0){
st2 = 1;
}
}
if(st1 && st2)
return 1;
if(!st1 && !st2)
e += res;
else if(!st1)
w += res;
else if(!st2)
q += res;
return 0;
}
void dfs(int u, long double res){
if(u == 1){
int minn = INF, idx1;
rep(i, 0, n - 1){
if(a[i].sum < minn && a[i].y > 0){
minn = a[i].sum;
idx1 = i;
}
}
a[idx1].sum ++;
long double k = 0;
rep(i, 0, m - 1){
if(b[i].y > 0)
k += 1;
}
res = res / k;
rep(i,0,m - 1){
if(b[i].y <= 0) continue;
a[idx1].y -= b[i].x;
b[i].y -= a[idx1].x;
if(check(res))
dfs(2, res);
a[idx1].y += b[i].x;
b[i].y += a[idx1].x;
}
a[idx1].sum --;
}
else{
int minn = INF, idx1;
rep(i, 0, m - 1){
if(b[i].sum < minn && b[i].y > 0){
minn = b[i].sum;
idx1 = i;
}
}
b[idx1].sum ++;
long double k = 0;
rep(i, 0, n - 1){
if(a[i].y > 0)
k += 1;
}
res = res / k;
rep(i,0,n - 1){
if(a[i].y <= 0) continue;
b[idx1].y -= a[i].x;
a[i].y -= b[idx1].x;
if(check(res))
dfs(1, res);
b[idx1].y += a[i].x;
a[i].y += b[idx1].x;
}
b[idx1].sum --;
}
}
void solve(){
cin >> n >> m;
rep(i,0,n - 1){
int k; cin >> k;
a[i].x = a[i].y = k;
}
rep(i,0,m - 1){
int k; cin >> k;
b[i].x = b[i].y = k;
}
if(n > m){
dfs(1, 1.0);
}
else if(m > n){
dfs(2, 1.0);
}
else{
dfs(1, 0.5);
dfs(2, 0.5);
}
cout << q << endl << w << endl << e << endl;
}
Problem F. Half Mixed
题意:构造一个n行m列的矩阵,纯子矩阵的数量等于混合子矩阵的数量,纯子矩阵就是全1或者全0,混合子矩阵就是01都有
思路:
首先是
N
O
NO
NO的情况:子矩阵个数总共有
n
∗
(
n
+
1
)
2
∗
m
∗
(
m
+
1
)
2
\frac{n * (n+1)}{2}*\frac{m*(m+1)}{2}
2n∗(n+1)∗2m∗(m+1) 个,所以这个数如果是计数肯定就不行了
然后是
Y
E
S
YES
YES 的情况
选择一个
x
∗
(
x
+
1
)
2
\frac{x * (x+1)}{2}
2x∗(x+1) 是偶数的边
假设同色的长度分别为
l
e
n
1
,
l
e
n
2
.
.
.
.
len_1,len_2....
len1,len2....,那么对于一个
1
∗
x
1*x
1∗x的矩形来说,纯色子区间的个数是
∑
i
=
1
l
e
n
i
∗
(
l
e
n
i
+
1
)
2
\sum_{i = 1}\frac{len_i * (len_i + 1)}{2}
∑i=12leni∗(leni+1)
从大到小枚
l
e
n
i
len_i
leni,满足以下两点
1、
∑
l
e
n
i
=
m
\sum len_i=m
∑leni=m
2、
∑
i
=
1
l
e
n
i
∗
(
l
e
n
i
+
1
)
2
=
x
∗
(
x
+
1
)
4
\sum_{i = 1}\frac{len_i * (len_i + 1)}{2} = \frac{x*(x+1)}{4}
∑i=12leni∗(leni+1)=4x∗(x+1)
code
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
const int N = 1e6 + 10;
int n, m;
int a[N], g[N];
void init(){
rep(i,1,N - 5)
a[i] = i * (i + 1) / 2;
}
void solve(){
cin >> n >> m;
if((n * (n + 1) / 2 * m * (m + 1) / 2) & 1 || ((n * (n + 1) / 2 & 1) && (m * (m + 1) / 2 & 1))){
cout << "No" << endl;
return;
}
cout << "Yes" << endl;
if((n * (n + 1) / 2) % 2 == 0){
int k = n * (n + 1) / 4 - n, sum = 0;
vector<int> v;
dec(i,n,1){
if(k == 0)
break;
int num = k / a[i];
rep(j,1,num){
sum += i + 1;
v.pb(i + 1);
}
k %= a[i];
}
sum = n - sum;
rep(i,1,sum)
v.pb(1);
int st = 0;
int idx = 1;
for(auto i : v){
rep(j, 0, i - 1){
rep(k, 1, m){
g[(idx + j - 1) * m + k] = st;
}
}
idx += i;
st ^= 1;
}
rep(i,1,n)
rep(j,1,m)
cout << g[(i - 1) * m + j] << " \n"[j == m];
}
else{
int k = m * (m + 1) / 4 - m, sum = 0;
vector<int> v;
dec(i,m,1){
int num = k / a[i];
rep(j,1,num){
sum += i + 1;
v.pb(i + 1);
}
k %= a[i];
if(!k) break;
}
sum = m - sum;
rep(i,1,sum)
v.pb(1);
int idx = 1, st = 0;
for(auto i : v){
rep(j,0,i - 1){
rep(k,1,n)
g[(k - 1) * m + idx + j] = st;
}
idx += i;
st ^= 1;
}
rep(i,1,n)
rep(j,1,m)
cout << g[(i - 1) * m + j] << " \n"[j == m];
}
}
Problem I. Quartz Collection
题意:alice和bob两个人选石头,每个人需要n种石头
下面有n种石头的两种价格,先是alice选一个,然后是bob和alice轮流选,每人选两个,直到最后剩一个,谁少给谁
问alice的集齐n种石头的最小花费
思路:
首先是考虑先选,优先选择的人收益最大,否则亏损最大
假设alice全选的
a
i
a_i
ai,那么选
b
i
b_i
bi 的话就是
∑
a
i
−
a
i
+
b
i
\sum a_i -a_i+b_i
∑ai−ai+bi
所以用线段树维护这个
b
i
−
a
i
b_i-a_i
bi−ai ,优先取负值
每个人可以取值两次,第一次和第二次取值的次数相邻,但是当前回合的第一次和下一回合的第一次相隔4次,所以需要
m
o
d
4
\mod 4
mod4
然后单调修改,区间查询的权值线段树
code
#define int long long
#define p1 (p << 1)
#define p2 (p << 1 | 1)
int ans;
int a[maxn], b[maxn];
struct seg
{
int l, r, siz, sum[4];
} tr[maxn << 3];
void pushup(int p)
{
tr[p].siz = tr[p1].siz + tr[p2].siz;
for (int i = 0; i < 4; i++)
tr[p].sum[i] = tr[p1].sum[i];
for (int i = 0; i < 4; i++)
tr[p].sum[(i + tr[p1].siz) % 4] += tr[p2].sum[i];
}
void modify(int p, int l, int r, int x, int op)
{
if (l == r)
{
int i = (tr[p].siz - (op == -1)) % 4;
tr[p].sum[i] += op * x;
tr[p].siz += op;
return;
}
else
{
int m = (l + r - 1) / 2;
if (x <= m)
modify(p1, l, m, x, op);
else
modify(p2, m + 1, r, x, op);
pushup(p);
}
}
void build(int p, int l, int r)
{
tr[p] = {l, r};
if (l == r)
return;
int m = (l + r - 1) / 2;
build(p1, l, m);
build(p2, m + 1, r);
pushup(p);
}
void get()
{
int siz = tr[2].siz, tp = ans;
ans -= tr[2].sum[1] + tr[2].sum[2];
if (siz % 2 == 0)
ans -= tr[3].sum[1] + tr[3].sum[3];
else
ans -= tr[3].sum[0] + tr[3].sum[2];
cout << ans << '\n';
ans = tp;
}
void solve()
{
int n, m;
cin >> n >> m;
build(1, -maxn, maxn);
for (int i = 1; i <= n; i++)
{
cin >> a[i] >> b[i];
modify(1, -maxn, maxn, a[i] - b[i], 1);
ans += a[i];
}
get();
while (m--)
{
int t, x, y;
cin >> t >> x >> y;
ans -= a[t];
modify(1, -maxn, maxn, a[t] - b[t], -1);
a[t] = x, b[t] = y;
ans += a[t];
modify(1, -maxn, maxn, a[t] - b[t], 1);
get();
}
}
Problem H. P-P-Palindrome
题意:给n个字符串,问字符串
s
i
s_i
si和字符串
s
j
s_j
sj的某个前缀首尾拼接而成的字符串是回文串,这个组合有多少个
思路:
利用回文树
构造出回文树之后,枚举边,就可以计算出最小循环节
假设循环节为k,
如果
l
e
n
m
o
d
k
=
0
len\mod k=0
lenmodk=0那么当前回文对答案的贡献是
l
e
n
/
k
∗
2
−
1
len/k*2-1
len/k∗2−1
否则对答案的贡献就是1
code
const int N = 1e6 + 10;
int tot = 1;
string s;
struct Node
{
int fail, ch[26], len;
}tr[N];
int get_fail(int x, int i){
while(s[i - tr[x].len - 1] != s[i]){
// cout << x << endl;
x = tr[x].fail;
}
return x;
}
void insert(){
cin >> s;
s = " " + s;
for(int i = 1, p = 0; i < s.size(); i ++){
// cout << p << endl;
p = get_fail(p, i);
if(!tr[p].ch[s[i] - 'a']){
tr[++ tot].fail = tr[get_fail(tr[p].fail, i)].ch[s[i] - 'a'];
tr[p].ch[s[i] - 'a'] = tot;
tr[tot].len = tr[p].len + 2;
}
p = tr[p].ch[s[i] - 'a'];
}
}
void solve(){
tr[0].fail = 1;
tr[1].len = -1;
int n;
cin >> n;
for(int i = 1; i <= n; i++)
insert();
ll ans = 0;
for(int i = 2; i <= tot; i ++){
ll k = tr[i].len - tr[tr[i].fail].len;
if(tr[i].len % k == 0)
ans += tr[i].len / k * 2 - 1;
else ans ++;
}
cout << ans << endl;
}