本場鏈接:[Codeforces Round #698 (Div. 2)]
閑話
超級自閉場
A. Nezzar and Colorful Balls
題目大意:有\(n\)個球序號從\(1\)開始,每個球上帶一個權值,所有球的權值按序號排列是非降序列.現在要給每個球畫一個顏色,要求每種顏色的球的權值是嚴格遞增的.
思路
由於范圍很小,可以直接拿數組記錄每個顏色下權值最大的值,每次遍歷每個顏色,看一下能不能把當前的球塞到序列末尾就可以了.
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
const int N = 105;
int a[N],c[N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
forn(i,1,n) scanf("%d",&a[i]),c[i] = 0;
forn(i,1,n)
{
forn(j,1,n)
{
if(c[j] < a[i])
{
c[j] = a[i];
break;
}
}
}
int res = 0;
forn(i,1,n) if(c[i]) ++res;
printf("%d\n",res);
}
return 0;
}
B. Nezzar and Lucky Number
題目大意:認為一個數是牛逼的,當且僅當這個數的十進制表示下存在一個數位為\(d\),給出\(q\)個詢問,每個詢問給定一個數\(a\),問其是否能表示成若干個牛逼數的和.
思路
這個題也是乍看上去非常難搞,因為很難用什么性質快速確定一個數是不是一個牛逼的數,而且一個牛逼的數的組合還很多,很復雜,他可以在前面加數也可以在后面加數,基本是很難搞的.
既然去找一個數是不是牛逼數很困難,不妨就直接拿\(d\)去拼出\(a\).設\(q = a % d\),那么當\(q=0\)時顯然可以表示成若干個\(d\)之和,否則需要考慮能否把這個\(q\)給湊成是一個牛逼的數:這里的形式就是把\(a\)拆成了\(q,d,d,d,d,d....d\).現在想要知道的問題就是對於\(q\)來說,他加上幾個\(d\)才能是一個牛逼的數,由於\(q,d\leq 9\),所以這件事情可以直接暴力預處理出來,\(jup[i][j]\)表示\(i\)這個數加上最少幾個\(d\)才能是一個牛逼的數.剩下的就是看這個\(a\)里面包含了幾個\(d\),當\(d\)的數量足夠\(q\)去拼成一個牛逼的數的時候,答案為YES
.
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
int jup[17][17];
int main()
{
forn(i,1,9)
{
forn(d,1,9)
{
int sum = i;
while(1)
{
sum += d;
int x = sum,ok = 0;
while(x)
{
if(x % 10 == d) ok = 1;
x /= 10;
}
++jup[i][d];
if(ok == 1) break;
}
}
}
// cout << jup[4][7] << endl;
int T;scanf("%d",&T);
while(T--)
{
int q,d;scanf("%d%d",&q,&d);
forn(i,1,q)
{
int a;scanf("%d",&a);
int l = a % d,have = a / d;
if(jup[l][d] <= have) puts("YES");
else puts("NO");
}
}
return 0;
}
C. Nezzar and Symmetric Array
題目大意:定義一個牛逼的數組,它的長度是\(2*n\),且每個元素互不相同,並且對任意一個\(a_i\)來說,這個數組里面都存在一個\(-a_i\).同時定義一個數組里面每個元素的牛逼值:\(d_i = \sum\limits_{j=1}^{2*n} |a_i - a_j|\),也就是每個元素和其他所有元素差的絕對值之和.現在只知道所有元素的牛逼值\(d\),問你構造一個滿足這個牛逼值的牛逼數組\(a\).不需要輸出具體形態,只需要YES
或者NO
.
要求\(a_i\neq 0\).
思路
雖然這個題目一看不需要輸出具體形態,很有可能有什么直接判斷的解,不過在這個題也沒什么用.
由於這個牛逼數組他的定義是一個鏡像的形態,我們不妨把正數部分摳出來,也就是把正數的前\(n\)項按從小到大的順序排列,那么如果能構造出來這個正數部分,負數部分既然就是前面的相反數,也就是搞一個\(0 < a_1 <a_2 < a_3 < ... < a_n\)出來,為了方便,\(a_{i+n}=-a_i\).那么對於任意一個\(d_i\)可以把表達式里的絕對值通過大小關系直接刪去,最后可以搞出一個式子:\(d_i = 2 * i * a_i + 2 * S_n - 2*S_i\),式中\(S_i\)表示\(a_i\)的前綴和,這個表達式可以做一個遞推:首先\(a_n\)的值可以直接算出來,其次\(d_{i+1} - d_i = 2 * i * (a_{i+1} - a_i)\),那么在知道\(a_{i+1}\)的前提下,可以推出他的前一位\(a_i\).那么剩下的就是從\(a_n\)往前遞推就可以了,需要判斷中途是否出現了違法情況.
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
const int N = 2e5+7;
ll d[N],a[N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
n = 2 * n;
map<ll,int> cnt;
forn(i,1,n) scanf("%lld",&d[i]),cnt[d[i]] ++;
int ok = 1;
forn(i,1,n) if(cnt[d[i]] != 2)
{
puts("NO");
ok = 0;
break;
}
if(!ok) continue;
sort(d + 1,d + n + 1);
vector<ll> n_d;n_d.push_back(0ll);
for(int i = 1;i <= n;i += 2) n_d.push_back(d[i]);
n /= 2;
forn(i,1,n) d[i] = n_d[i];
if(d[n] % (2 * n))
{
puts("NO");
continue;
}
cnt.clear();
a[n] = d[n] / (2 * n);cnt[a[n]] ++;
forr(i,1,n - 1)
{
ll up = d[i + 1] - d[i],dw = 2 * i;
if(!dw || up % dw)
{
ok = 0;
puts("NO");
break;
}
a[i] = a[i + 1] - up / dw;
if(a[i] <= 0 || cnt.count(a[i]))
{
ok = 0;
puts("NO");
break;
}
cnt[a[i]] ++;
}
if(ok) puts("YES");
}
return 0;
}
D. Nezzar and Board
題目大意:有\(n\)個互不相同的數\(x\),每次可以從所有數里面選出兩個數\(x,y\),加入一個新的數\(2*x-y\),注意不會這個操作不會把原來的\(x,y\)兩個元素刪掉.問是否能得到目標值\(k\).
思路
如果把這個操作\(2 * x - y\)看做是$x + x - y \(,那么就可以把整個過程看作是任取一個元素\)x\(並且加上任意兩個數的差,這個差可以帶正數符號也可以帶負數符號.其次可以注意到像\)x_3-x_1\(這樣的差可以被\)x_3-x_2\(和\)x_2-x_1\(這樣的差表示出來,也就是說只需要考慮形如\)x_i - x_{i-1}\(這樣的差,構造一個數組\)a\(:\)a_i = x_i - x_{i-1}\((刪去\)x_1\().那么現在的問題就是任取\)x_i\(中的某個元素,讓他加上\)k_1a_1 + k_2 * a_2....k_na_n\(這樣的式子,問他是否能得到目標值.先不管\)x_i\(,考慮后面這坨表達式能湊出什么東西:\)n\(個項的裴蜀定理,他能表示的值一定是\)gcd(a_1,a_2,...a_n)\(的倍數(就是兩項的拓展).那么現在問題就是,記\)g\(是整個數組\)a\(的最大公約數,問題就是是否存在某個\)x_i\(有\)x_i+z * g == k\(式中\)z\(是任意整數.那么直接對每個\)x_i$檢查就可以了.
對於這個題來說,還有一個點,其實只用檢查\(x_1\)就可以了,因為對於其他的情況,可以加上某個\(a_i\)來代替.
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
const int N = 2e5+7;
ll x[N];
ll gcd(ll x,ll y)
{
if(y == 0) return x;
return gcd(y,x % y);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
ll n,k;scanf("%lld%lld",&n,&k);
forn(i,1,n) scanf("%lld",&x[i]);
sort(x + 1,x + n + 1);
ll g = x[2] - x[1];
forn(i,3,n) g = gcd(g,x[i] - x[i - 1]);
if((k - x[1]) % g == 0) puts("YES");
else puts("NO");
}
return 0;
}
E. Nezzar and Binary String
題目大意:一開始有一個二進制串\(s\),一共有\(q\)次詢問,每個詢問給定一個區間\([l,r]\),以及兩個過程:首先看這個區間里面是否同時含有\(0,1\),如果有則會立即停止過程,如果都是一樣的,則可以修改這個區間里面嚴格小於區間長度一半的元素.問經過\(q\)次詢問,且不能中途退出的前提下,是否能把一開始的\(s\)變成另外一個二進制串\(f\).
思路
正着做很難搞,考慮反着做:
所有的操作逆過來之后,就等於是在考慮先修改不超過區間長度一半的元素,之后要求修改之后的整個區間同色.做完了所有詢問之后,看整個串是不是和目標串相同.那么修改什么呢?因為要保證整個區間同色,所以其實所有修改操作全部都是把較少的顏色直接修改成較多的顏色,不存在說要去判斷跟目標串的距離.那么一個區間推平,且能區間查詢元素個數的線段樹就可以了.
對於中途退出的判斷,只會在整個區間長度是偶數,並且兩個顏色的個數相同的時候引發,此時直接輸出NO
即可.
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
typedef pair<int,int> pii;
#define x first
#define y second
const int N = 2e5+7;
struct Node
{
int l,r;
int v,lazy;
}tr[N * 4];
pii inv[N];
char SST[N],EED[N];
void pushup(int u)
{
tr[u].v = tr[u << 1].v + tr[u << 1 | 1].v;
}
void pushdown(int u)
{
auto& s = tr[u],&lf = tr[u << 1],&rt = tr[u << 1 | 1];
if(~s.lazy)
{
lf.v = (lf.r - lf.l + 1) * s.lazy;
lf.lazy = s.lazy;
rt.v = (rt.r - rt.l + 1) * s.lazy;
rt.lazy = s.lazy;
s.lazy = -1;
}
}
void build(int u,int l,int r)
{
if(l == r) tr[u] = {l,r,EED[l] - '0',-1};
else
{
int mid = l + r >> 1;
tr[u] = {l,r,0,-1};
build(u << 1,l,mid),build(u << 1 | 1,mid + 1,r);
pushup(u);
}
}
int query(int u,int l,int r)
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u].v;
int mid = tr[u].l + tr[u].r >> 1,res= 0;
pushdown(u);
if(l <= mid) res += query(u << 1,l,r);
if(r > mid) res += query(u << 1 | 1,l,r);
return res;
}
void modify(int u,int l,int r,int v)
{
if(tr[u].l >= l && tr[u].r <= r)
{
tr[u].v = v * (tr[u].r - tr[u].l + 1);
tr[u].lazy = v;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if(l <= mid) modify(u << 1,l,r,v);
if(r > mid) modify(u << 1 | 1,l,r,v);
pushup(u);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,q;scanf("%d%d",&n,&q);
scanf("%s%s",SST + 1,EED + 1);
forn(i,1,q) scanf("%d%d",&inv[i].x,&inv[i].y);
build(1,1,n);
int ok = 1;
forr(i,1,q)
{
int c1 = query(1,inv[i].x,inv[i].y),c0 = inv[i].y - inv[i].x + 1 - c1;
if(c0 == c1)
{
ok = 0;
break;
}
modify(1,inv[i].x,inv[i].y,c0 < c1);
}
forn(i,1,n) if(query(1,i,i) != SST[i] - '0') {ok = 0;break;}
if(ok) puts("YES");
else puts("NO");
}
return 0;
}
F. Nezzar and Nice Beatmap
題目大意:平面上有\(n\)個互不相同的點,每三個連續的點會產生一個角,要求重排給定的點集合的順序,使每個角都是銳角.
對於第一個元素和最后一個元素不構成角的情況忽略.
思路
假如說有個鈍角三角形,那么鈍角對應的一定是最大的那一條邊.現在把三條邊去掉一條,留下兩條構成一個角,如果構成的是鈍角,那么肯定是把較短的兩個選上了,為了避免選出一個鈍角,直覺就是從某個點出發,每次鏈接的點都是當前點距離最遠的點.
假如說我們拿任意一個點作為起點,並且拿他去找另外一個最遠點,再把這個兩個點的連線作為半徑畫一個圓,形成的圓可以包含所有的點,並且從起點連向的另外一個點,再去連任何一個點都不會形成一個鈍角:一旦形成鈍角,根據三角形兩邊之和的規律,與這個點是最遠點矛盾了.那么每次都按最大值取,就可以保證每次都是銳角了.
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
typedef pair<int,int> pii;
#define x first
#define y second
const int N = 5005;
pii a[N];
bool st[N];
ll dist(const pii& a,const pii& b)
{
ll dx = a.x - b.x,dy = a.y - b.y;
return dx * dx + dy * dy;
}
int main()
{
int n;scanf("%d",&n);
forn(i,1,n) scanf("%d%d",&a[i].x,&a[i].y);
st[1] = 1;printf("1 ");
int c = 1;
forn(i,2,n)
{
ll maxd = -1e18;
int t = -1;
forn(j,1,n)
{
if(st[j]) continue;
ll d = dist(a[c],a[j]);
if(d > maxd)
{
maxd = d;
t = j;
}
}
if(t == -1) break;
st[t] = 1;
c = t;
printf("%d ",t);
}
return 0;
}