思维自闭场
C. Nezzar and Symmetric Array
思路
给一个整数 n, 给定一个 d 数组, 长度为 (2 * n) 而 d 数组是由一个未知的 a 数组通过每一位元素与其他元素距离之和的绝对值得来的,且 a 数组元素都是唯一,两两对应,一个数和其相反数,判断是否存在该 a 数组。
首先将 d 数组排序,然后我们可以通过 d 数组是由 a 数组的距离绝对值得来且 a 数组两两对应,可以推测出 d 数组中所有元素都应该是偶数,且也是两两对应,每个数都出现两次。
然后可以从最大的 d 开始构造 a 数组,最大的 d 数组元素 k 所对应的 a 数组元素 z 存在其余元素与其对应元素到 z 的距离之和都为 (2 * z) 的性质, 那么就有 (z = k / (2 * n)),然后d的第二大元素 w 其对应 a 数组元素 y,发现其余元素与其对应元素到 y 的距离之和为 (2 * y),除了大于 y 的 z 元素为 (2 * z) 外,所以可以先将 (w - 2 * z),然后就有 (y = w / (2 * n - 2)),因为少了最大的 z 和其相反数 -z 两个元素,所以要减2,依此类推,推出整个 a 数组,然后判断 a 数组是否合法即可
代码
#include <bits/stdc++.h>
using namespace std;
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
typedef long long ll;
const int N = 2e5 + 10;
ll d[N];
map<ll , int> mp;
ll n , k , res;
bool check(){
bool flag = true;
for(int i = 1;i <= 2 * n ; i += 2)
if(d[i] != d[i + 1] || d[i] & 1) flag = false;
return flag;
}
void solve(){
cin >> n;
mp.clear();
for(int i = 1;i <= n * 2;i++) cin >> d[i];
sort(d + 1 , d + 1 + n * 2);
if(!check()) {
puts("NO") ;
return ;
}
for(int i = 1 , j = 1;i <= n;i ++ , j += 2)
d[i] = d[j];
ll tmp = 0 , cnt = 2 * n;
for(int i = n; i >= 1;i --){
d[i] -= tmp;
if(d[i] % cnt != 0 || d[i] <= 0 || mp[d[i] / cnt]){
puts("NO");
return ;
}
mp[d[i] / cnt] ++;
tmp += 2 * d[i] / cnt;
cnt -= 2;
}
puts("YES");
}
int main(){
int t;
cin >> t;
while(t--)
solve();
return 0;
}
D. Nezzar and Board
思路
给 n 个数,和一个目标数 k ,每次操作选取两个数 x 和 y,然后得到一个新的数为 (2 * x - y),问是否可以通过操作获得目标数 k。可以将其想像成一个坐标轴,然后(2 * x - y) 就是 y 关于坐标值为 x 的地方对称。那么我们可以将 k 当作原点,其余元素都与 k 一起平移。然后就是求是否通过操作可以得到 0。每个数与其他数的距离就是每次操作可以改变的距离,所有距离的gcd就是操作可以改变的最小距离,而且我们可以只求相邻点的距离的gcd,因为其他点的值可以通过两个距离相加得到。最后只要判断每个点,是否为这个最短操作距离的倍数,如果为这个距离的倍数就表示这个点可以通过若干次操作,到达原点
代码
#include <bits/stdc++.h>
using namespace std;
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
typedef long long ll;
const int N = 2e5 + 10;
ll n , k;
ll a[N];
ll gcd(ll a, ll b){
return b ? gcd(b , a % b) : a;
}
void solve(){
cin >> n >> k;
for(int i = 1;i <= n;i++) cin >> a[i] , a[i] -= k;
sort(a + 1, a + 1 + n);
ll g = a[2] - a[1];
for(int i = 2;i <= n;i++) g = gcd(g , a[i] - a[i - 1]);
for(int i = 1;i <= n;i++)
if(a[i] % g == 0){
puts("YES");
return ;
}
puts("NO");
}
int main(){
int t;
cin >> t;
while(t--)
solve();
return 0;
}
E. Nezzar and Binary String
思路
给两个指定长度的字符串,仅包含0或者1。q次询问,每次询问先判断这个区间的字串是否只包含一种元素,若不是,则直接失败,否则就可以对该区间进行操作,可以改变严格小于给定区间长度一半的元素,由0边为1,判断q次询问后是否能由第一个字符串得到第二个字符串。
那么首先正着想这个问题肯定是不好处理的,要判断区间是否只包含一种元素很容易,但是修改哪些元素就不好抉择。我们可以反向看这个问题,从目标串出发,然后给一个区间,就是问这个区间的元素是否可以从一个只包含一种元素的区间转化过来,因为反着看就是先操作后判断。很显然,对于一个区间只要不是1和0的个数相等,就一定可以从一个只包含一种元素的区间转变而来,如果0多就全部变为0,否则全部变为1,而0和1相等的转变就不满足操作数严格小于区间长度的一半。q次询问结束后,去判断当前的字符串是否等于起始字符串即可。
那么处理这个问题,如果单纯的用数组处理,每次操作都要整个区间遍历的判断和一个个的修改,时间复杂度会很高。可以发现这是一个序列长度不变,然后每次操作都是连续的一段区间的问题,那么我们可以用线段树来处理这个东西。也许可以用分块??不太会就没试,有兴趣的可以自己试下。
代码
#include <bits/stdc++.h>
using namespace std;
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
typedef long long ll;
const int N = 2e5 + 10;
struct Node {
int l , r , v , lazy;
}tr[N * 4];
char s[N] , e[N];
int l[N] , r[N];
void update(int p){
tr[p].v = tr[p << 1].v + tr[(p << 1) + 1].v ;
}
void build(int p , int l , int r){
tr[p] = {l , r , 0 , -1};
if(l == r){
tr[p].v = e[l] - '0';
return ;
}
int mid = (l + r) >> 1;
build(p << 1 , l , mid) ;
build((p << 1) + 1 , mid + 1 , r);
update(p);
}
void push_down(int p){
if(~tr[p].lazy){
tr[p << 1].v = tr[p].lazy * (tr[p << 1].r - tr[p << 1].l + 1);
tr[(p << 1) + 1].v = tr[p].lazy * (tr[(p << 1) + 1].r - tr[(p << 1) + 1].l + 1);
tr[p << 1].lazy = tr[p].lazy;
tr[(p << 1) + 1].lazy = tr[p].lazy;
tr[p].lazy = -1;
}
}
void add(int p ,int l ,int r , int k){
if(tr[p].l == l && tr[p].r == r){
tr[p].v = k * (tr[p].r - tr[p].l + 1);
tr[p].lazy = k;
return ;
}
push_down(p);
int mid = (tr[p].l + tr[p].r) >> 1;
if(l > mid) add((p << 1) + 1 , l , r , k);
else if(r <= mid) add(p << 1 , l , r , k);
else add(p << 1 , l , mid , k) , add((p << 1) + 1 , mid + 1 , r , k);
update(p);
}
int query(int p , int l , int r){
if(tr[p].l == l && tr[p].r == r)
return tr[p].v;
push_down(p);
int res = 0;
int mid = (tr[p].l + tr[p].r) >> 1;
if(l > mid) res += query((p << 1) + 1 , l , r);
else if(r <= mid) res += query(p << 1 , l , r);
else res += query(p << 1 , l , mid) , res += query((p << 1) + 1 , mid + 1 , r);
return res ;
}
int n , m;
void solve(){
cin >> n >> m;
cin >> s + 1 >> e + 1;
for(int i = 1;i <= m;i++) cin >> l[i] >> r[i];
build(1 ,1 ,n);
bool flag = true;
for(int i = m ; i >= 1;i --){
int one = query(1 , l[i] , r[i]);
int zero = r[i] - l[i] + 1 - one;
if(one == zero){
flag = false;
break;
}
if(zero > one)
add(1 , l[i] , r[i] , 0);
else
add(1 , l[i] , r[i] , 1);
}
for(int i = 1;i <= n;i++) if(query(1, i ,i) != s[i] - '0') flag = false;
if(flag) puts("YES");
else puts("NO");
}
int main(){
int t;
cin >> t;
while(t--)
solve();
return 0;
}