CF1771
CF1771A
CF1771A题意
有一个长度为 n n n 的序列 a 1 , 2 , . . . , n a_{1,2,...,n} a1,2,...,n,需要求出这个序列中一共有多少对点对满足
- 1 ≤ i , j ≤ n 1\le i,j\le n 1≤i,j≤n
- i ≠ j i\neq j i=j
- ∣ a i − a j ∣ = max 1 ≤ p , q ≤ n ∣ a p − a q ∣ |a_i-a_j|=\max_{1\le p,q\le n}|a_p-a_q| ∣ai−aj∣=max1≤p,q≤n∣ap−aq∣
CF1771A题解
不难发现
max
1
≤
p
,
q
≤
n
∣
a
p
−
a
q
∣
\max_{1\le p,q\le n}|a_p-a_q|
max1≤p,q≤n∣ap−aq∣ 其实就是序列中最大值减去最小值
然后又发现只有当
a
i
,
a
j
a_i,a_j
ai,aj 分别是最大值和最小值的时候才能满足这个柿子
然后就没有然后了
不过需要注意一下,如果整个序列所有数相同的话,答案是
n
×
(
n
−
1
)
n\times (n-1)
n×(n−1) 需要特判
CF1771A代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e5 + 10;
int n;
signed main(){
int T = read();
while(T--){
n = read();int mx = -1,mxtime = 0,mn = 0x3f3f3f3f,mntime = 0,u;
for(int i = 1;i <= n;i++){
u = read();
if(u > mx){mxtime = 1;mx = u;}
else if(u == mx){mxtime++;}
if(u < mn){mntime = 1;mn = u;}
else if(u == mn){mntime++;}
}
if(mn != mx)printf("%lld\n",mxtime * mntime * 2ll);
else printf("%lld\n",n * (n - 1));
}
return 0;
}
CF1771B
CF1771B题意
给出
n
,
m
n,m
n,m 和
m
m
m 个限制
x
i
,
y
i
x_i,y_i
xi,yi(注意,不保证
x
i
≤
y
i
x_i\le y_i
xi≤yi)
现在要求求出满足以下要求的点对
i
,
j
i,j
i,j 的数量
- 1 ≤ i ≤ j ≤ n 1\le i\le j\le n 1≤i≤j≤n
- 不 ∃ k , i ≤ x k , y k ≤ j \exist k,i\le x_k,y_k\le j ∃k,i≤xk,yk≤j
CF1771B题解
这个题其实很简单,考虑从
n
→
1
n\to 1
n→1 的枚举
i
i
i,统计有多少个
i
≤
j
i\le j
i≤j 满足要求
不妨让
x
i
≤
y
i
x_i\le y_i
xi≤yi
发现只要
j
j
j 小于所有的
y
k
(
i
≤
x
k
)
y_k(i\le x_k)
yk(i≤xk),那么这个
j
j
j 就是合法的
同时发现这玩应对于一个
i
i
i,只要
j
j
j 满足,那么
i
≤
k
≤
j
i\le k\le j
i≤k≤j 都会满足
然后就没了所以为什么翻译里面没说
x
i
,
y
i
x_i,y_i
xi,yi 的关系啊(
CF1771B代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e5 + 10;
int n, m;
vector<int> vec[maxn];
signed main(){
int T = read();
while(T--){
n = read();m = read();int u, v;
for(int i = 1;i <= n;i++)vec[i].clear();
for(int i = 1;i <= m;i++){u = read(); v = read();vec[min(u,v)].push_back(max(u,v));}
int lst = n + 1,ans = 0;
for(int i = n;i;i--){
if(!vec[i].empty()){for(int j : vec[i])lst = min(lst,j);}
ans += lst - i;
}
printf("%lld\n",ans);
}
return 0;
}
CF1771C
CF1771C题意
有一个长度为 n n n 的数组 a 1 , 2 , . . . , n a_{1,2,...,n} a1,2,...,n,问这个数组中是否存在一对点 i , j i,j i,j 满足 gcd ( a i , a j ) ≠ 1 \gcd(a_i,a_j) \neq 1 gcd(ai,aj)=1
CF1771C题解
很自然想到将每一个数字都质因数分解,然后放入 m a p map map 中,如果有相同的就 “YES”
CF1771C代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e5 + 10;
int n, a[maxn];
int prime[maxn], tot;
bool book[maxn];
map<int,int> mp;
signed main(){
for(int i = 2;i <= 1e5;i++){
if(!book[i]){prime[++tot] = i;}
for(int j = 1;j <= tot && i * prime[j] <= 1e5;j++){
book[i * prime[j]] = 1;
if(i % prime[j] == 0)break;
}
}
int T = read();
while(T--){
n = read();mp.clear();
for(int i = 1;i <= n;i++){a[i] = read();}
bool flag = false;
for(int i = 1;i <= n && !flag;i++){
int tmp = a[i];
for(int j = 1;j <= tot && prime[j] <= tmp;j++){
if(tmp % prime[j] == 0){
if(mp[prime[j]] == 1){flag = true;break;}
mp[prime[j]] = 1;
while(tmp % prime[j] == 0)tmp /= prime[j];
}
}
if(tmp != 1){
if(mp[tmp]){flag = true;break;}
mp[tmp] = 1;
}
}
puts(flag ? "YES" : "NO");
}
return 0;
}
CF1771D
CF1771D题意
给出一棵树 T T T(节点数 ≤ 2 × 1 0 3 \le 2\times10^3 ≤2×103),每个树上有一个字母,问所有路径上的最大回文串子序列长是多少。
CF1771D题解
先考虑下序列上的怎么做。
设
f
i
,
j
f_{i,j}
fi,j 表示
[
i
,
j
]
[i,j]
[i,j] 的最大回文串子序列长度。
那么有
f
i
,
j
=
max
f
i
+
1
,
j
,
f
i
,
j
−
1
,
f
i
+
1
,
j
−
1
+
(
c
h
i
=
=
c
h
j
)
×
2
f_{i,j} = \max{f_{i+1,j},f_{i,j-1},f_{i+1,j-1}+(ch_i==ch_j)\times 2}
fi,j=maxfi+1,j,fi,j−1,fi+1,j−1+(chi==chj)×2
那树上的怎么办呢?
不妨设
f
a
i
,
j
fa_{i,j}
fai,j 表示当这棵树以
j
j
j 为根时
i
i
i 的父亲节点。
那么显然有
f
i
,
j
=
max
f
f
a
i
,
j
,
j
,
f
i
,
f
a
j
,
i
,
f
f
a
i
,
j
,
f
a
j
,
i
+
(
c
h
i
=
=
c
h
j
)
×
2
f_{i,j} = \max{f_{fa_{i,j},j},f_{i,fa_{j,i}},f_{fa_{i,j},fa_{j,i}}+(ch_i==ch_j)\times 2}
fi,j=maxffai,j,j,fi,faj,i,ffai,j,faj,i+(chi==chj)×2
更新顺序?
序列上的时候按照两点距离就好了,排下序就完了。
然后就会喜提
T
L
E
o
n
T
e
s
t
15
TLE\space on\space Test15
TLE on Test15(
为什么呢,因为
O
(
n
2
×
log
2
n
)
O(n^2\times \log_2n)
O(n2×log2n) 肥肠难以通过
那怎么办呢?
想到,如果想要更新一对点
(
i
,
j
)
(i,j)
(i,j),就必须更新所有这条路径上的点。
然而这玩应可以记忆化搜索解决
然后就没有然后了
CF1771D代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
// int x = 0, f = 1;char ch = getchar();
// while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
// while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
// return x * f;
int x;scanf("%d",&x);return x;
}
const int maxn = 2e3 + 10;
int n;char ch[maxn];
int fa[maxn][maxn], f[maxn][maxn];
vector<int> edg[maxn];
void dfs(int u,int fat,int root){
fa[u][root] = fat;
for(int v : edg[u])
if(v != fat){dfs(v, u,root);}
}
int calc(int u,int v){
if(u == v)return 1;
if(u == fa[v][1] || v == fa[u][1]){return 1 + (ch[u] == ch[v]);}
if(f[u][v])return f[u][v];
return f[u][v] = max(calc(u,fa[v][u]),max(calc(fa[u][v],v),calc(fa[u][v],fa[v][u]) + (ch[u] == ch[v] ? 2 : 0)));
}
signed main(){
// #ifndef ONLINE_JUDGE
// freopen("test.in","r",stdin);
// #endif
int T = read();
while(T--){
n = read();scanf("%s",ch + 1);
for(int i = 1;i <= n;i++){edg[i].clear();}
for(int i = 1;i < n;i++){
int u = read(), v = read();
edg[u].push_back(v);edg[v].push_back(u);
}
for(int i = 1;i <= n;i++){
dfs(i,i,i);
for(int j = 1;j <= n;j++)f[i][j] = 0;
}
int ans = 0;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
ans = max(ans,calc(i,j));
printf("%d\n",ans);
}
return 0;
}
CF1771E
CF1771E题意
给出一个地图(
n
×
m
n\times m
n×m,其中
1
≤
n
,
m
≤
400
1\le n,m\le 400
1≤n,m≤400),其中有三种字符 “
.
.
.”,“
#
\#
#” 和 “
m
m
m”
要求你在这张地图中画出一个 "
H
H
H"字,具体要求如下
- " H H H"字的左上角和右上角需要在同一行上,左下角和右下角需要在同一行上
- " H H H"字不能经过 “ # \# #”
- " H H H"字至多能经过一个 “ m m m”
- " H H H"字可以经过任意个 “ . . .”
请问怎样画 “ H H H” 字使得它所覆盖的点最大
CF1771E题解
种花?
这不就是种CCF的翻版吗
发现这个数据范围很小,可以不用乱七八糟的预处理,也不用考虑预处理的时间复杂度
设
u
p
i
,
j
,
0
/
1
up_{i,j,0/1}
upi,j,0/1 表示从
(
i
,
j
)
(i,j)
(i,j) 点向上走 经过/不经过 “
m
m
m” 能走多远
设
d
o
w
n
i
,
j
,
0
/
1
down_{i,j,0/1}
downi,j,0/1 表示从
(
i
,
j
)
(i,j)
(i,j) 点向下走 经过/不经过 “
m
m
m” 能走多远
注意,这里的预处理不包括点
(
i
,
j
)
(i,j)
(i,j)
然后更新答案的时候直接暴力枚举 “
H
H
H” 字的左右交点,然后维护一下交点之间有没有 “
m
m
m”,没有的话可以把经过的机会依次给四个竖线比一下就好了
细节是真的多吃我好几发提交(
CF1771E代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 400 + 10;
int n, m;char ch[maxn][maxn];
int up[maxn][maxn][2], down[maxn][maxn][2];
signed main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++){scanf("%s",ch[i] + 1);}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
bool passed = 0, stop = 0;
up[i][j][0] = up[i][j][1] = down[i][j][0] = down[i][j][1] = 0;
for(int k = i - 1;k && !stop;k--){
switch(ch[k][j]){
case '.':
if(!passed)up[i][j][0]++;
up[i][j][1]++;
break;
case '#':
stop = 1;
break;
case 'm':
if(passed){stop = 1;break;}
passed = 1; up[i][j][1]++;
break;
default:break;
}
}
passed = 0;stop = 0;
for(int k = i + 1;k <= n && !stop;k++){
switch(ch[k][j]){
case '.':
if(!passed)down[i][j][0]++;
down[i][j][1]++;
break;
case '#':
stop = 1;
break;
case 'm':
if(passed){stop = 1;break;}
passed = 1; down[i][j][1]++;
break;
default:break;
}
}
if(!up[i][j][0])up[i][j][0] = -114514;
if(!up[i][j][1])up[i][j][1] = -114514;
if(!down[i][j][0])down[i][j][0] = -114514;
if(!down[i][j][1])down[i][j][1] = -114514;
}
}
int ans = 0;
for(int i = 1;i <= n;i++){
for(int j = 3;j <= m;j++){
bool passed = (ch[i][j] == 'm');
if(ch[i][j] == '#' || ch[i][j - 1] == '#')continue;
if((ch[i][j - 1] == 'm')){if(passed)continue;passed = 1;}
for(int k = j - 2;k;k--){
if(ch[i][k] == '#')break;
if(ch[i][k] == 'm'){if(passed)break;passed = 1;}
int up1 = min(up[i][k][0],up[i][j][0]);
int down1 = min(down[i][k][0],down[i][j][0]);
if(!passed){
int up2 = max(min(up[i][k][0],up[i][j][1]),min(up[i][k][1],up[i][j][0]));
int down2 = max(min(down[i][k][0],down[i][j][1]),min(down[i][k][1],down[i][j][0]));
ans = max(ans,j - k + 1 + 2 * max(up1 + down2,up2 + down1));
}
ans = max(ans,j - k + 1 + 2 * (up1 + down1));
}
}
}
printf("%d\n",ans);
return 0;
}
CF1771F
CF1771F题意
给出一个非负整数序列
a
1
,
2
,
.
.
.
,
n
a_{1,2,...,n}
a1,2,...,n
每次询问
[
l
,
r
]
[l,r]
[l,r] 区间内出现次数为奇数的最小数字,没有扣眼珠子输出0
强制在线
CF1771F题解
首先先想一下如果没有出现次数是奇数的限制怎么做。
还用废话啊主席树就完了呗。
但是有了这个限制怎么办呢。
有么有一种方法,能够让一个数出现偶数次就凭空消失呢。
x
o
r
xor
xor!
如果一个数
x
o
r
xor
xor 自己偶数次,那就会凭空消失,而
x
o
r
xor
xor 自己奇数次却不会。
那这就好办了,搞一个可持久化权值线段树分治,每个位置维护的是自己所在区间的
x
o
r
xor
xor 值,查询的时候只需要判断一下左儿子的
x
o
r
xor
xor 值是不是
0
0
0,如果不是就进入左儿子,是就进入右儿子。
然后你就开心的交了上去。
…
\space
\space
\space
\space
\space
\space
\space
\space
\space
\space
恭喜!
你
W
A
WA
WA 了这道题!
为啥呢是不是自己哪里写挂了?
在调了
1
′
14
5
′
14
1
′
91
9
′
810
s
1'145'141'919'810s
1′145′141′919′810s 后你不禁想到了一个问题:
- 1 x o r 2 x o r 3 = 0 1\space xor \space 2 \space xor \space 3 = 0 1 xor 2 xor 3=0
?!xor你演我
那肿莫办呢?原神,启动!
那好办,给每个值在线段树上
x
o
r
xor
xor 的时候
x
o
r
xor
xor 一个随机数,这样就不会出现被演的情况了。
CF1771F代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 2e5 + 10, mod = (1 << 30);
int n, m;
int a[maxn];
map<int,int> mp;
int head[maxn];
struct Segment_Tree{
int tot;
struct node{
int lson, rson, sum;
node(int sum = 0,int lson = 0,int rson = 0):lson(lson),rson(rson),sum(sum){}
}d[maxn * 100];
node mergenode(node l,node r,node f = node()){return node(l.sum ^ r.sum,f.lson,f.rson);}
int update(int lst,int pos,int x,int l = 1,int r = mod){
int u = ++tot;d[u] = d[lst];
if(l == r){d[u].sum ^= x;return u;}
int mid = l + r >> 1;
if(pos <= mid)d[u].lson = update(d[lst].lson,pos,x,l,mid);
else d[u].rson = update(d[lst].rson,pos,x,mid + 1,r);
d[u] = mergenode(d[d[u].lson],d[d[u].rson],d[u]);
return u;
}
int query(int now,int lst,int l = 1,int r = mod){
if(l == r){return d[now].sum ^ d[lst].sum ? l : 0;}
int mid = l + r >> 1, tmp = d[d[now].lson].sum ^ d[d[lst].lson].sum;
if(tmp)return query(d[now].lson,d[lst].lson,l,mid);
else return query(d[now].rson,d[lst].rson,mid + 1,r);
}
}tree;
signed main(){
n = read();
mt19937 rand(time(0));
for(int i = 1;i <= n;i++){
a[i] = read();
if(mp.count(a[i]))continue;
mp[a[i]] = (rand() & mod - 1) + 1;
}
for(int i = 1;i <= n;i++){head[i] = tree.update(head[i - 1],a[i],mp[a[i]]);}
m = read();int l, r, lastans = 0;
for(int i = 1;i <= m;i++){
l = read() ^ lastans; r = read() ^ lastans;
printf("%d\n",lastans = tree.query(head[r],head[l - 1]));
}
return 0;
}