A.Anti Light’s Cell Guessing
题目描述
一个n×mn\times mn×m的矩形,有一个位置(i,j)(i,j)(i,j)是特殊的。
每次可以询问一个点(x,y)(x,y)(x,y),返回(i,j)(i,j)(i,j)到(x,y)(x,y)(x,y)的欧几里得距离,问至少要询问多少次对任意(i,j)(i,j)(i,j)都能确认
分析
对于大多数的点我们只要询问(1,1)(1,1)(1,1)和(n,1)(n,1)(n,1)即可
代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)
{
int n,m;
cin>>n>>m;
if (n==1&&m==1)cout<<"0\n";
else if (min(n,m)==1)cout<<"1\n";
else cout<<"2\n";
}
}
B. Kalindrome Array
题目描述
给一个数组,问从数组中删除掉一种数字或者不删除能不能构成回文数组
分析
删除掉的那以这种数字一定是我们左右匹配时,遇到的第一对不匹配的两种数字中的一种
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
int a[maxn];
int n;
bool check(int num)
{
for (int i=1,j=n;i<j;++i,--j)
{
while (i<n&&a[i]==num)++i;
while (j>i&&a[j]==num)--j;
if (a[i]!=a[j])return false;
}return true;
}
void solve()
{
cin>>n;
for (int i=1;i<=n;++i)cin>>a[i];
for (int i=1,j=n;i<j;++i,--j)
{
if (a[i]!=a[j])
{
if (check(a[i])||check(a[j]))
{
cout<<"YES\n";
}else cout<<"NO\n";
return;
}
}cout<<"YES\n";
}
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)solve();
}
C. Keshi Is Throwing a Party
题目描述
现在要开宴会,我们要邀请尽量多的人
对于一个人iii,他要求邀请的人中比他富有的人不超过a[i]a[i]a[i],比他贫穷的人不超过b[i]b[i]b[i]
我们知道所有人富有值的升序排列
分析
我们可以直接二分答案midmidmid,然后贪心地从穷到富地去选人,维护已经选的人数curcurcur
对于某一个人,如果他之前选的curcurcur不超过b[i]b[i]b[i],然后剩下的mid−cur−1≥a[i]mid-cur-1\ge a[i]mid−cur−1≥a[i]
那么我们就选择这个人
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
int a[maxn],b[maxn];
int n;
bool check(int m)
{
int cur = 0;
for (int i=1;i<=n;++i)
{
if (b[i]>=cur&&a[i]>=m-cur-1)
++cur;
if (cur==m)return true;
}
return false;
}
void solve()
{
cin>>n;
for (int i=1;i<=n;++i)cin>>a[i]>>b[i];
int l = 1,r = n;
int ans = 1;
while (l<=r)
{
int mid = l+r>>1;
if (check(mid))
{
ans = mid;l = mid+1;
}else r = mid-1;
}cout<<ans<<"\n";
}
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)solve();
}
D. Not Quite Lee
题目描述
给一个长度为nnn的正整数数组aaa ,每一个数代表了一个长为aia_iai的连续数字,具体从哪里开始任意
要求统计,有多少子序列有可能使得每一段连续数字之和的和为000
分析
对于aia_iai,如果他被选中了,那么提供的贡献就是ki×ai+ai×(ai+1)2k_i \times a_i+\frac{a_i \times (a_i +1 )}{2}ki×ai+2ai×(ai+1)
其中kik_iki是首项。
那么,如果我们选择了的序列为[b1,b2,b3…bm][b_1,b_2,b_3\dots b_m][b1,b2,b3…bm]
那么实际上就是k1×b1+k2×b2+k3×b3+⋯+km×bm=b1×(b1+1)2+b2×(b2+1)2+b3×(b3+1)2+⋯+bm×(bm+1)2k_1\times b_1 +k_2 \times b_2 +k_3\times b_3 +\dots +k_m\times b_m = \frac{b_1\times (b_1+1)}{2}+\frac{b_2\times (b_2+1)}{2}+\frac{b_3\times (b_3+1)}{2}+\dots +\frac{b_m\times (b_m+1)}{2}k1×b1+k2×b2+k3×b3+⋯+km×bm=2b1×(b1+1)+2b2×(b2+1)+2b3×(b3+1)+⋯+2bm×(bm+1)
这很容易就让我们想到了贝祖定理,我们去思考gcd(b1,b2,…,bm)gcd(b_1,b_2,\dots ,b_m)gcd(b1,b2,…,bm)与右式的整除关系
首先一个结论,如果存在奇数,那么等式一定有解!
因为,若bib_ibi存在奇数,那么gcdgcdgcd一定是奇数。故对于右式,bi×(bi+1)2\frac{b_i\times (b_i+1)}{2}2bi×(bi+1)
-
若bib_ibi为偶数,那么bib_ibi一定可以整除gcdgcdgcd,同时,因为gcdgcdgcd为奇数,所以不会消耗bib_ibi的222的质因子
,因此bib_ibi同时还可以整除222
-
若bib_ibi为奇数,那么bib_ibi一定可以整除gcdgcdgcd,同时,bi+1b_i+1bi+1一定可以整除222
我们只要考虑全为偶数的情况了。
gcdgcdgcd的定义是,对于每一个质因子,找到其所有数中的最小指数幂,就是gcdgcdgcd中的指数幂
那么,我们将gcdgcdgcd表示为2t×p2^t\times p2t×p,则ttt是所有bbb中222的最小指数幂
我们认为那么对于右式,我们尝试让右式去整除2t×p2^t\times p2t×p
注意到,如果bib_ibi的222的指数幂大于ttt的话,那么bi×(bi+1)2\frac{b_i\times (b_i+1)}{2}2bi×(bi+1)一定可以整除
剩下的就是bib_ibi的222的指数幂恰巧等于ttt的情况了,要想能够整除,他们必须俩俩合并
因此,222的指数幂为ttt的数一定要是偶数!
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
const ll mod = 1e9+7;
ll J[maxn],p2[maxn];
ll POW(ll base,ll p)
{
ll ans=1;
while (p)
{
if (p&1)ans = ans*base%mod;
base = base*base%mod;
p>>=1;
}return ans;
}
ll inv(ll num)
{
return POW(num,mod-2);
}
ll C(int n,int m)
{
return J[n]*inv(J[m]*J[n-m]%mod)%mod;
}
int a[maxn],b[50];
int n;
void solve()
{
cin>>n;
for (int i=1;i<=n;++i)cin>>a[i];
for (int i=1;i<=n;++i)if (a[i]%2==0)
{
int cnt=0;
while (a[i]%2==0)
{
++cnt;
a[i]>>=1;
}b[cnt]++;
}
ll ans = (p2[n]+mod-1)%mod;
int sum = 0;
for (int i=35;i>=1;sum+=b[i],--i)
for (int j=1;j<=b[i];j+=2)ans = (ans-C(b[i],j)*p2[sum]%mod+mod)%mod;
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);
J[0]=p2[0]=1;
for (int i=1;i<maxn;++i)J[i]=i*J[i-1]%mod,p2[i]=(p2[i-1]<<1LL)%mod;
solve();
}
E.AmShZ and G.O.A.T.
题目描述
若一个数组中,元素的平均值是 avgavgavg,比 avgavgavg 小的元素的个数小于比 avgavgavg 大的元素的个数,则称数组是 terrible 的。
删去数组的一些元素,使得该数组的所有子数组都不是 terrible 的,求最少需要删除几个元素?
数据范围:ttt 组数据,1≤t≤1041\leq t\leq 10^41≤t≤104,数组长度 2≤n≤2×1052\leq n\leq 2\times 10^52≤n≤2×105,数组元素 1≤ai≤1091\leq a_i\leq 10^91≤ai≤109,保证对于 1≤i<n1\leq i<n1≤i<n,有 ai≤ai+1a_i\leq a_{i+1}ai≤ai+1,nnn 的和不超过 2×1052\times 10^52×105。
分析
考虑最简单的情况,如果三个数字 a,b,c(a<b<c)a,b,c(a<b<c)a,b,c(a<b<c) 有 b−a>c−bb-a>c-bb−a>c−b,则数组是 terrible 的。如果数组中每三个元素都不满足这样的条件,则它不是 terrible 的。
枚举删除后,数组中剩下的最小的元素 xxx。
保留所有的 xxx,后面的元素至少为 a1=x+1a_1=x+1a1=x+1,则 a2−a1≥a1−xa_2-a_1\geq a_1-xa2−a1≥a1−x,即 a2≥2a1−xa_2\geq 2a_1-xa2≥2a1−x,找出最小的符合条件的 a2a_2a2,然后求 a3,a4,⋯a_3,a_4,\cdotsa3,a4,⋯,直到枚举到数组的末尾。
枚举 xxx 的时间复杂度为 O(n)O(n)O(n),但求解 a2,a3,a4,⋯a_2,a_3,a_4,\cdotsa2,a3,a4,⋯ 至多只有 O(log109)O(\log 10^9)O(log109) 次,因此可以通过。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
map<int, int> mp;
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
mp[x]++;
}
int ans = 0;
for (auto[key, val]: mp) {
int now = key, s = 1;
int cnt = val;
while (true) {
auto it = mp.lower_bound(now + s);
if (it == mp.end())
break;
cnt++;
now = it->first;
s = it->first - key;
}
ans = max(ans, cnt);
}
cout << n - ans << endl;
}
return 0;
}
F.Mashtali: a Space Oddysey
题目描述
有一个 nnn 个点 mmm 条边的无向图(1≤n≤105,1≤m≤1051\leq n\leq 10^5,1\leq m\leq 10^51≤n≤105,1≤m≤105),图有可能不是联通的,有可能包含重边,每条边的边权要么是 111 要么是 222。把每条边都改成有向的,使图中 ∣d+(v)−d−(v)∣=1|d^+(v)-d^-(v)|=1∣d+(v)−d−(v)∣=1 的点(好点)的数量尽可能多(d+(v)d^+(v)d+(v) 为向点 vvv 连边的边权和,d−(v)d^-(v)d−(v) 为点 vvv 连向其他边的边权和),求调整后最多有多少个好点,并按顺序输出每条边连接的方向。
分析
容易发现一个性质:对于任意一条边,无论是哪个方向,都会对两端的点造成 222 或 444 的影响,因此如果某个点和它相连的边的边权和为偶数,则不可能将这个点调整为好点。
考虑能否构造出一种方案,使得所有边权和为奇数的点都可以成为好点。
将所有的奇度数点都与一个虚点相连一条边权为 111 的虚边。一条边会同时改变两点的度数奇偶情况,即奇度数点变为偶数。所以我们操作之后一定所有点的度数都是偶数,那这个图是一个欧拉回路。
在进行欧拉回路的遍历时,我们首先满足让出边让入边的边权相等,否则使用另一边。
边权和为偶数的可以任意定向,不进行考虑。
边权和为奇数的,有以下两种情况:
-
奇数条边权为 111 的边,奇数条的边权为 222 的边
按照我们的操作方法,一定会最后只抵消到只剩一条边权为 111 的和一条边权为 222 的边,该点为好点。
-
奇数条边权为 111 的边,偶数条的边权为 222 的边
按照我们的操作方法,最后会抵消到只剩一条边权为 111,该点为好点。
所以我们只要按照这种操作方法,即可使得所有边权和的奇数的点都是好点。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 600010;
int n, m;
int to[N], from[N], head[N][3];
int fans;
int vis[N], ans[N], cnt[N], dfn[N], sum[N];
vector<int> edge[N][3];
inline void dfs(int u, int now) {
int fnow = now;
while (!(edge[u][now].size() <= head[u][now] && edge[u][3 - now].size() <= head[u][3 - now])) {
dfn[u] = 1;
while (head[u][now] < edge[u][now].size() && vis[edge[u][now][head[u][now]]])
head[u][now]++;
while (head[u][3 - now] < edge[u][3 - now].size() && vis[edge[u][3 - now][head[u][3 - now]]])
head[u][3 - now]++;
if (edge[u][now].size() != head[u][now]) {
ans[edge[u][now][head[u][now]]] = (u == from[edge[u][now][head[u][now]]]) + 1;
vis[edge[u][now][head[u][now]]] = 1;
dfs(to[edge[u][now][head[u][now]]] - u, now);
head[u][now]++;
} else {
now = 3 - now;
if (edge[u][now].size() != head[u][now]) {
ans[edge[u][now][head[u][now]]] = (u == from[edge[u][now][head[u][now]]]) + 1;
vis[edge[u][now][head[u][now]]] = 1;
dfs(to[edge[u][now][head[u][now]]] - u, now);
head[u][now]++;
}
}
now = fnow;
}
return;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; ++i) {
int x, y, w;
scanf("%d %d %d", &x, &y, &w);
to[i] = x + y;
cnt[x]++;
cnt[y]++;
from[i] = x;
sum[x] += w;
sum[y] += w;
edge[x][w].push_back(i);
edge[y][w].push_back(i);
}
int mcnt = m;
for (int i = 1; i <= n; ++i) {
if (sum[i] & 1)
fans++;
if (cnt[i] & 1) {
int x = n + 1;
int y = i;
int w = 1;
to[++mcnt] = x + y;
from[mcnt] = x;
edge[x][w].push_back(mcnt);
edge[y][w].push_back(mcnt);
}
}
for (int i = 1; i <= n + 1; ++i)
if (!dfn[i])
dfs(i, 1);
cout << fans << endl;
for (int i = 1; i <= m; ++i)
cout << ans[i];
cout << endl;
return 0;
}
本文介绍了四道算法题目,包括最少询问次数确定特殊位置、判断数组是否能通过删除一个数字变成回文、最大邀请人数满足条件的宴会、计算子序列和为零的个数以及如何定向图中的边以最大化好点数量。涉及欧几里得距离、回文判断、二分查找、贝祖定理和图的遍历等算法思想。
1262

被折叠的 条评论
为什么被折叠?



