Codeforces Round #639 (Div. 2)
A.Puzzle Pieces
如果只有一行或者一列是必然可以的,如果多余两行(列)的话,另一维度必须小于等于二
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
void solve(){
int n, m;
cin >> n >> m;
if(min(n,m)==1 or max(n,m)<=2) cout << "YES" << endl;
else cout << "NO" << endl;
}
int main(){
____(); int T;
for(cin >> T; T; T--) solve();
return 0;
}
B.Card Constructions
找出需要木棍的数量的公式
高度每增加\(1\),需要木棍的数量增加\(3\cdot i - 1\)
所以对于某个高度,需要的木棍的数量为\(\sum_{i=1}^{h}3\cdot i - 1 = \frac{n+3n^2}{2}\)
这个增加速度挺快,所以可以不断二分当前的木棍数量可以构成的最高高度
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
using LL = int_fast64_t;
LL f(LL x){ return x*(1+3*x)/2; }
int search(LL x){
LL l = 1, r = sqrt(x);
while(l<=r){
LL mid = (l+r) >> 1;
if(f(mid)<=x) l = mid + 1;
else r = mid - 1;
}
return f(r);
}
void solve(){
LL n;
cin >> n;
int ret = 0;
while(n>=2){
n -= search(n);
ret++;
}
cout << ret << endl;
}
int main(){
int T;
____(); for(cin >> T; T; T--) solve();
return 0;
}
C.Hilbert's Hotel
其实就是循环给每个数\(i\)加上\(A_{i\%n}\)
循环节是\(n\)
每个循环中的每个数比上一次大\(n\)
为了不出现重复,只要计算是否存在两个数模\(n\)同余即可
注意处理负数
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e5+7;
int n;
void solve(){
cin >> n;
set<int> S;
bool ok = true;
for(int i = 0; i < n; i++){
int x; cin >> x;
x += i; x = (x % n + n) % n;
if(S.count(x)) ok = false;
S.insert(x);
}
if(ok) cout << "YES" << endl;
else cout << "NO" << endl;
}
int main(){
____();
int T;
for(cin >> T; T; T--) solve();
return 0;
}
D.Monopole Magnets
可以发现每个黑色格子都是可以放一个S极磁铁的,然后对于某个位置,如果这一行这一列都没有黑色格子,那么这个位置放上S极磁铁是没有关系的,现在我们最大化的放了S极磁铁,判断一下是否每行每列都有S极磁铁,如果存在一行或者一列没有S极磁铁,直接输出\(-1\)
然后为了让N极磁铁不能到白色格点,那么同一行或者同一列的两个黑色格子之间是不能存在白色格点的,这个可以通过这一行最左最右的黑色格点位置和这一行的黑色格点数量来判断(列同理)
最后如果可行,答案就是连通块的数量
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1111;
const int dir[4][2] = {{-1,0},{0,-1},{1,0},{0,1}};
char s[MAXN][MAXN];
bool ox[MAXN],oy[MAXN],vis[MAXN][MAXN];
int cntx[MAXN],cnty[MAXN],lmax[MAXN],rmax[MAXN],umax[MAXN],dmax[MAXN];
int n,m,ckx[MAXN],cky[MAXN];
bool checkok(){
for(int i = 1; i <= n; i++) if(!ckx[i]) return false;
for(int i = 1; i <= m; i++) if(!cky[i]) return false;
return true;
}
void bfs(int x, int y){
queue<pair<int,int> > que;
que.push(make_pair(x,y));
vis[x][y] = true;
while(!que.empty()){
auto p = que.front();
que.pop();
int cx = p.first, cy = p.second;
for(int i = 0; i < 4; i++){
int nx = cx + dir[i][0];
int ny = cy + dir[i][1];
if(nx<1 or ny<1 or nx>n or ny>m or vis[nx][ny] or s[nx][ny]=='.') continue;
vis[nx][ny] = true;
que.push(make_pair(nx,ny));
}
}
}
int main(){
scanf("%d %d",&n,&m);
vector<pair<int,int> > vec;
for(int i = 1; i <= n; i++) scanf("%s",s[i]+1);
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++){
if(s[i][j]=='.') continue;
ox[i] = oy[j] = true;
ckx[i] = cky[j] = true;
vec.emplace_back(make_pair(i,j));
}
for(int i =1 ; i <= n; i++) for(int j = 1; j <= m; j++){
if(!ox[i] and !oy[j]) ckx[i] = cky[j] = true;
}
if(!checkok()){
cout << -1 << endl;
return 0;
}
for(auto p : vec){
cntx[p.first] = cnty[p.second] = 0;
lmax[p.first] = umax[p.second] = MAXN;
rmax[p.first] = dmax[p.second] = 0;
}
for(auto p : vec){
cntx[p.first]++; cnty[p.second]++;
lmax[p.first] = min(lmax[p.first],p.second);
rmax[p.first] = max(rmax[p.first],p.second);
umax[p.second] = min(umax[p.second],p.first);
dmax[p.second] = max(dmax[p.second],p.first);
}
for(auto p : vec){
int x = p.first, y = p.second;
if(cntx[x]!=rmax[x]-lmax[x]+1){
cout << -1 << endl;
return 0;
}
if(cnty[y]!=dmax[y]-umax[y]+1){
cout << -1 << endl;
return 0;
}
}
int tot = 0;
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++){
if(s[i][j]=='.' or vis[i][j]) continue;
tot++;
bfs(i,j);
}
cout << tot << endl;
return 0;
}
E.Quantifier Question
首先建图,如果\(x_i<x_j\),那么从\(x_i\)向\(x_j\)连一条边,如果存在环说明矛盾,则不存在解,否则必然存在解
因为和顺序有关,假设\(x_i和x_j\)存在大小关系,如果\(i<j\),由于下标小的首先确定,所以下标大的必然不能是\(universal\),只能是\(existential\),只有下标小的那个有可能是\(universal\),因为现在是个\(DAG\),所以只要通过拓扑序正反两遍\(DP\),就能找到和每个数有大小关系的数的最小下标,如果这个最小下标就是自己,那么这个数就是可以是\(universal\)的
```//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e5+7;
int n,m,deg[MAXN],rdeg[MAXN],f[MAXN],rf[MAXN],vis[MAXN];
vector<int> G[MAXN],rG[MAXN];
bool A[MAXN];
bool haveCircle(){
queue<int> que;
for(int i = 1; i <= n; i++) if(!deg[i]) que.push(i);
while(!que.empty()){
int u = que.front();
que.pop();
for(int v : G[u]){
if(!--deg[v]) que.push(v);
f[v] = min(f[v],f[u]);
}
}
for(int i = 1; i <= n; i++) if(deg[i]) return true;
for(int i = 1; i <= n; i++) if(!rdeg[i]) que.push(i);
while(!que.empty()){
int u = que.front();
que.pop();
for(int v : rG[u]){
if(!--rdeg[v]) que.push(v);
rf[v] = min(rf[v],rf[u]);
}
}
return false;
}
int main(){
scanf("%d %d",&n,&m);
for(int i = 1; i <= n; i++) f[i] = rf[i] = i;
for(int i = 1; i <= m; i++){
int u, v;
scanf("%d %d",&u,&v);
G[u].emplace_back(v);
rG[v].emplace_back(u);
deg[v]++; rdeg[u]++;
}
if(haveCircle()){
puts("-1");
return 0;
}
int num = 0;
for(int i = 1; i <= n; i++) if(min(f[i],rf[i])==i){
num++;
A[i] = true;
}
printf("%d\n",num);
for(int i = 1; i <= n; i++) putchar(A[i]?'A':'E');
return 0;
}
F.Résumé Review
假设一开始所有都没有选,我们计算选每个位置能得到的贡献是多少
\(Contribution = x(a_i-x^2) - (x-1)(a_i-(x-1)^2) = -x^2+3x+a_i-1\)
在大于等于\(1\)时这个函数是单调递减的
然后我们每次贪心选取贡献最大的,但是由于\(k\)的范围很大,需要换一种方式
考虑二分一个数\(delta\),考虑将所有贡献值大于等于\(delta\)的值计算进来
也就是说,对于每次二分的\(delta\),对于每个位置,我们计算最大的\(B_i\),使得\(-B_i^2+3B_i+A_i-1>=delta\)(这里可以用求根公式也可二分)
然后判断\(\sum_{i=1}^{n} B_i\)是否小于等于\(k\)
最后取到的答案可能总和\(tot\)不到\(k\),需要继续选\(k-tot\)个使总和到达\(k\),这时候\(k-tot\)不超过\(n\),枚举每个下标,选取最小的前\(tot-k\)个贡献值加上去就好了
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
typedef long long int LL;
const int MAXN = 1e5+7;
LL n,A[MAXN],B[MAXN];
LL k;
//-3x^2 + 3x + a - 1
inline LL f(LL x, LL a){ return -3 * x * x + 3 * x + a - 1; }
int calmax(int p, LL m){
int l = 1, r = A[p];
while(l<=r){
int mid = (l + r) >> 1;
if(f(mid,A[p])>=m) l = mid + 1;
else r = mid - 1;
}
return r;
}
bool check(LL m){
LL tot = 0;
for(int i = 1; i <= n; i++) tot += calmax(i,m);
return tot <= k;
}
int main(){
____();
cin >> n >> k;
for(int i = 1; i <= n; i++) cin >> A[i];
LL l = -4e18, r = 1e9;
while(l<=r){
LL mid = (l+r) >> 1;
if(check(mid)) r = mid - 1;
else l = mid + 1;
}
LL tot = 0;
vector<pair<LL,int> > vec;
for(int i = 1; i <= n; i++){
tot += B[i] = calmax(i,l);
if(B[i]<A[i]) vec.emplace_back(make_pair(f(B[i]+1,A[i]),i));
}
sort(vec.begin(),vec.end(),greater<pair<LL,int>>());
tot = k - tot;
for(int i = 0; i < tot; i++) B[vec[i].second]++;
for(int i = 1; i <= n; i++) cout << B[i] << ' ';
cout << endl;
return 0;
}