Vjudge 3.14 训练解题报告
A. Fibonacci-ish
题意:定义一个序列为“Fibonacci-ish”的,当且仅当对任意
。给定一个长为 的数组,求选出若干个元素重新排列,形成“Fibonacci-ish”的序列的最长长度。 。
首先有一个暴力做法,
By zhouhaoxu
#include<bits/stdc++.h>
#define int long long
#define N 200005
#define pb push_back
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
int n,a[N],ans; map<int,int>cn; vector<int>vi;
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),cn[a[i]]++,ans+=(a[i]==0);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
if(i==j) continue; if(a[i]==0&&a[j]==0) continue; vi.clear(),vi.pb(a[i]),vi.pb(a[j]);
cn[a[i]]--,cn[a[j]]--; int p=a[i]+a[j],ret=2,lst=a[j]; while(p<=(int)2e9&&p>=(int)-2e9&&cn[p]){
cn[p]--,vi.pb(p); int t=lst+p; lst=p,p=t,++ret;
}for(int &u:vi) cn[u]++; ans=max(ans,ret);
}return !printf("%lld\n",ans);
}
B. Grime Zoo
题意:给定一个含问号的 01 串,你需要将所有问号改为 0 或 1,定义代价为 01 的子序列数
加上 10 的子序列数 。最小化代价。
直接考虑非常复杂,所以考虑挖掘性质。可以猜想,如果 01 的代价较大,则要尽可能把前面填 1,后面填 0;反之则尽可能前面填 0,后面填 1。
这种贪心看起来比较假,但实际上不难证明为真:假设 01 的代价
更感性一点的证明是,交换两位对左右两边来说没有影响,因为在他们看来,都是同一侧有一个 0 一个 1,交换位置没有影响。但对中间来说,一个是左 0 右 1,一个是左 1 右 0,自然后者更优。
于是,我们假设要左边填 1 右边填 0(反之则 01 取反即可),考虑如何计算答案。首先预处理出数字与数字之间的贡献
预处理
By cxm1024
#include <bits/stdc++.h>
using namespace std;
#define int long long
int s1[100010], s0[100010], s2[100010];
int l[100010], r[100010];
signed main() {
string s;
int x, y;
cin >> s >> x >> y;
int n = s.size();
s = " " + s;
if (y > x) {
for (int i = 1; i <= n; i++) {
if (s[i] == '1') s[i] = '0';
else if (s[i] == '0') s[i] = '1';
}
swap(x, y);
}
for (int i = 1; i <= n; i++) {
s1[i] = s1[i - 1] + (s[i] == '1');
s0[i] = s0[i - 1] + (s[i] == '0');
s2[i] = s2[i - 1] + (s[i] == '?');
}
int res = 0;
for (int i = 1; i <= n; i++)
if (s[i] == '1')
res += s0[i - 1] * x + (s0[n] - s0[i]) * y;
for (int i = 1; i <= n; i++) {
l[i] = l[i - 1];
if (s[i] == '?') l[i] += s0[i - 1] * x + (s0[n] - s0[i]) * y;
}
for (int i = n; i > 0; i--) {
r[i] = r[i + 1];
if (s[i] == '?') r[i] += (s1[n] - s1[i]) * x + s1[i - 1] * y;
}
int ans = r[1];
for (int i = 1; i <= n; i++) {
if (s[i] == '?') ans = min(ans, l[i] + r[i + 1] + s2[i] * (s2[n] - s2[i]) * y);
}
cout << ans + res << endl;
return 0;
}
C. Vasily the Bear and Sequence
题意:给一个长度为
的数组,你需要选出一些元素,使他们按位与后的 lowbit
尽量大(定义lowbit(0)=-1
),在此基础上元素尽可能多。
显然可以枚举钦定 lowbit
,然后判定是否可行。如果 lowbit
钦定为第
By cxm1024
#include <bits/stdc++.h>
using namespace std;
int n, a[100010];
signed main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
vector<int> ans;
int maxn = -1;
for (int i = 0; i <= 30; i++) {
vector<int> v;
for (int j = 1; j <= n; j++)
if (a[j] >> i & 1) v.push_back(a[j]);
if (v.empty()) continue;
int res = v[0];
for (int j : v) res &= j;
if ((res & -res) > maxn)
ans = v, maxn = (res & -res);
}
printf("%lu\n", ans.size());
for (int x : ans) printf("%d ", x);
return 0;
}
D. Mike and Frog
题意:有两个变量,第
秒为 ,每秒 ,问最早使 且 的时刻是多少。
显然问题的关键在于形成的环。两个元素都会在若干步之后进入环,所以可以暴力跑
假设两个环长分别为
看起来需要使用 exgcd 来解同余方程,但其实并不需要,可以如下操作:
因为
By zhouhaoxu
#include<bits/stdc++.h>
#define int long long
#define N 2000005
#define pb push_back
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
int l[2],m,h[2],a[2],x[2],y[2],p[2],t[N],vs[N],ls[N],th[2];
signed main(){
scanf("%lld",&m); for(int i=0;i<2;i++)
scanf("%lld%lld%lld%lld",&h[i],&a[i],&x[i],&y[i]),th[i]=h[i];
for(int t=0;t<=m;t++){
if(th[0]==a[0]&&th[1]==a[1]) return !printf("%lld\n",t);
for(int i=0;i<2;i++) th[i]=(th[i]*x[i]+y[i])%m;
}for(int i=0;i<2;i++){
memset(vs,0,sizeof(vs)),memset(t,0,sizeof(t));
int u=h[i]; while(!vs[u]) t[u]=(l[i]++),vs[u]=1,u=(x[i]*u+y[i])%m;
int va=u; u=h[i]; ls[i]=t[va];
for(int tp=0;tp<t[va];tp++) l[i]--,vs[u]=0,u=(x[i]*u+y[i])%m;
if(!vs[a[i]]) return !printf("-1"); p[i]=t[a[i]]-t[va];
}for(int i=0;i<=l[1];i++) if((l[0]*i+p[0]+ls[0]-ls[1])%l[1]==p[1])
return !printf("%lld\n",(l[0]*i+p[0]+ls[0]-ls[1]));
return !printf("-1");
}
E. Hidden Word
题意:一个
的矩阵,每个格子填一个字母。两个格子相邻按照八连通判定。现给定一个长度 的字符串,且每个字母至少出现一次,构造一个填字母的方法,使该字符串能对应矩阵中一个路径。
显然问题的关键在于那个重复的字母如何经过两次。观察样例可以发现,可以如下构造:
此时该字母的两次出现之间隔了偶数个字母。如果隔了奇数个,可以如下构造:
对于左边的填法,字母多的行绕到另一行补齐一下即可。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
bool vis[26];
char a[2][13];
signed main() {
string s;
cin >> s;
char now = 'A', flag;
for (int i = 0; i < s.size(); i++) {
if (!vis[s[i] - 'A'])
vis[s[i] - 'A'] = s[i];
else flag = s[i];
}
vector<int> v;
for (int i = 0; i < s.size(); i++)
if (s[i] == flag) v.push_back(i);
if (v[0] + 1 == v[1]) puts("Impossible");
else {
int x = 0, y = 13 - (v[1] - v[0] - 1) / 2;
a[x][y - 1] = flag;
if (y == 13) y--, x++;
for (int j = v[0] + 1; j < v[1]; j++) {
a[x][y] = s[j];
if (x == 0) y++;
else y--;
if (y == 13) y--, x++;
}
a[0][13 - (v[1] - v[0] - 1) / 2 - 2] = flag;
x = 0, y = 13 - (v[1] - v[0] - 1) / 2 - 2;
if (y < 0) y = 0, x = 1;
for (int j = v[1] + 1; j < s.size(); j++) {
a[x][y] = s[j];
if (x == 0) y--;
else y++;
if (y < 0) x++, y = 0;
}
x = 1, y = 13 - (v[1] - v[0]) / 2 - 1;
if (y < 0) y = 0, x = 0;
for (int j = v[0] - 1; j >= 0; j--) {
a[x][y] = s[j];
if (x == 1) y--;
else y++;
if (y < 0) x--, y = 0;
}
for (int i = 0; i <= 1; i++) {
for (int j = 0; j < 13; j++)
cout << a[i][j];
cout << endl;
}
}
return 0;
}
F. Video Cards
题意:有
个元素,你可以选一个元素作为中心元素,其他元素中选若干个作为附属元素,要求附属元素必须为中心元素的倍数,否则可以通过减小附属元素来调整至合法。注意中心元素不能调整。求和最大的合法方案。
容易发现附属元素必然可以全选(最差也可以减少到
容易发现在
By cxm1024
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, a[200010], s[200010];
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
s[a[i]]++;
}
for (int i = 1; i <= 200000; i++)
s[i] += s[i - 1];
long long ans = 0;
for (int i = 1; i <= 200000; i++) {
if (s[i] - s[i - 1] == 0) continue;
long long res = 0;
for (int j = i; j <= 200000; j += i)
res += (s[min(200000ll, j + i - 1)] - s[j - 1]) * (j / i);
ans = max(ans, 1ll * i * res);
}
printf("%lld\n", ans);
return 0;
}
G. Masha-forgetful
题意:给定
个模式串 和一个文本串 ,长度均为 ,字符集为 。定义一个 的子串合法,当且仅当长度 且在某个 中出现过。构造将 划分为合法子串的方案或判断无解。
显然对于一个合法的字符串,其所有长度
By zhouhaoxu
#include<bits/stdc++.h>
#define N 200005
#define pb push_back
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
int f[N],T,n,m,pr[N]; map<string,pii >st;
bool is(string s){return st.find(s)!=st.end();}
struct node{int l,r,k;}; vector<node>ans;
void calc(string u){
pii p=st[u]; ans.pb((node){p.se,p.se+(int)u.size()-1,p.fi});
}string s,t; signed main(){
ios::sync_with_stdio(false),cin>>T;
while(T--){
cin>>n>>m,st.clear(),ans.clear();
for(int i=1;i<=n;i++){
cin>>s; for(int j=0;j<m;j++){
if(j+1<m) st[s.substr(j,2)]=make_pair(i,j);
if(j+2<m) st[s.substr(j,3)]=make_pair(i,j);
}
}cin>>t; for(int i=0;i<=m;i++) f[i]=pr[i]=0;
if(m==1){cout<<-1<<endl; continue;}
if(m>1) f[1]=is(t.substr(0,2));
if(m>2) f[2]=is(t.substr(0,3));
for(int i=1;i<m;i++){
if(!f[i]) continue;
if(i+2<m&&is(t.substr(i+1,2))) f[i+2]=1,pr[i+2]=i;
if(i+3<m&&is(t.substr(i+1,3))) f[i+3]=1,pr[i+3]=i;
}if(!f[m-1]) cout<<-1<<endl; else{
int r=m-1;
while(r>2) calc(t.substr(pr[r]+1,r-pr[r])),r=pr[r];
calc(t.substr(0,r+1)); reverse(ans.begin(),ans.end());
cout<<(int)ans.size()<<endl;
for(auto &u:ans) cout<<u.l+1<<' '<<u.r+1<<' '<<u.k<<endl;
}
}return 0;
}
如果没有注意到段长必须为 2 或 3,其实也可以做。设
要对每个位置找到最长的出现过的后缀,可以使用 SAM 维护。首先将
注意转移时
By cxm1024
#include <bits/stdc++.h>
using namespace std;
string t;
struct node {
int fa, nxt[11], len, tag, r;
node() {
fa = len = tag = r = 0;
memset(nxt, 0, sizeof(nxt));
}
} a[2000010];
int lst = 1, cnt = 1;
void insert(int x, int y, int z) {
int p = lst, now = ++cnt;
a[now] = node();
a[now].len = a[lst].len + 1, a[now].tag = y, a[now].r = z;
for (; p && a[p].nxt[x] == 0; p = a[p].fa)
a[p].nxt[x] = now;
int q = a[p].nxt[x];
if (q == 0) a[now].fa = 1;
else if (a[p].len + 1 == a[q].len) a[now].fa = q;
else {
int r = ++cnt;
a[r] = node();
a[r] = a[q], a[r].len = a[p].len + 1;
for (; p && a[p].nxt[x] == q; p = a[p].fa)
a[p].nxt[x] = r;
a[q].fa = a[now].fa = r;
}
lst = now;
}
int f[1010], g[1010], pre[1010];
int rr[1010], ii[1010];
void Solve(int test) {
lst = cnt = 1, a[1] = node();
int n, m;
cin >> n >> m;
for (int i = 0; i <= m; i++) {
f[i] = g[i] = 0;
pre[i] = rr[i] = ii[i] = 0;
}
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
for (int j = 0; j < s.size(); j++)
insert(s[j] - '0', i, j + 1);
insert(10, i, 0);
}
cin >> t;
t = " " + t;
int now = 1, l = 1;
if (a[now].nxt[t[1] - '0']) now = a[now].nxt[t[1] - '0'];
else l = 2;
for (int i = 2; i <= m; i++) {
while (now != 1 && a[now].nxt[t[i] - '0'] == 0)
l = i - a[a[now].fa].len, now = a[now].fa;
g[i] = g[i - 1];
if (a[now].nxt[t[i] - '0'] == 0) l = i + 1;
if (a[now].nxt[t[i] - '0']) {
now = a[now].nxt[t[i] - '0'];
if (l == 1) {
f[i] = 1, pre[i] = 0;
rr[i] = a[now].r, ii[i] = a[now].tag;
g[i] = i;
}
else if (g[i - 2] >= l - 1) {
f[i] = 1, pre[i] = (i == 0 ? -1 : g[i - 2]);
rr[i] = a[now].r, ii[i] = a[now].tag;
g[i] = i;
}
}
}
if (!f[m]) cout << -1 << endl;
else {
vector<array<int, 3> > ans;
int now = m;
while (now) {
ans.push_back({rr[now] - (now - pre[now]) + 1, rr[now], ii[now]});
now = pre[now];
}
reverse(ans.begin(), ans.end());
cout << ans.size() << endl;
for (auto [x, y, z] : ans)
cout << x << " " << y << " " << z << endl;
}
while (cnt) a[cnt--] = node();
for (int i = 0; i <= m; i++) {
f[i] = g[i] = 0;
pre[i] = rr[i] = ii[i] = 0;
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
for (int _test = 1; _test <= T; _test++)
Solve(_test);
return 0;
}
H. Nearest Fraction
题意:给定一个分数
,求一个分数 使得 ,且与 尽可能接近。如有相同则取较小的 。
由于
By zhouhaoxu
#include<bits/stdc++.h>
#define int long long
#define N 200005
#define pb push_back
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
int x,y,n,a0=-1,b0=-1; double ans=1;
signed main(){
scanf("%lld%lld%lld",&x,&y,&n);
for(int i=1;i<=n;i++){
int l=0,r=(int)1e10,ret=0; while(l<=r){
int md=(l+r)>>1; if(md*y<=i*x) ret=md,l=md+1;
else r=md-1;
}for(int j=max(ret-2,0ll);j<=ret+2;j++){
double va=fabs(1.0*x/y-1.0*j/i);
if(a0==-1){ans=va,a0=j,b0=i; continue;}
if(ans-va>1e-15) ans=va,a0=j,b0=i;
else if(fabs(ans-va)<1e-15){
if(b0>i) b0=i,a0=j; else if(b0==i) a0=min(a0,j);
}
}
}return !printf("%lld/%lld\n",a0,b0);
}
事实上,最接近的分子可以直接得出,而无需二分答案。当
By AmirAz
#include <iostream>
#include <cstdio>
#include <set>
#include <cmath>
using namespace std;
typedef long long ll;
bool comp(ll a, ll b, ll x1, ll y1, ll x2, ll y2){
return (abs(a * y1 * y2 - x1 * b * y2) < abs(a * y2 * y1 - x2 * b * y1));
}
int main(){
ll a, b, n; cin >> a >> b >> n;
ll minx = 1e7 + 10, miny = 1;
for (ll i = 1; i <= n; i++){
ll y = i;
ll x = floor((long double) y * a / b);
if (comp(a,b, x, y, minx, miny)){
minx = x;
miny = y;
}
x = ceil((long double)y * a / b);
if (comp(a,b, x, y, minx, miny)){
minx = x;
miny = y;
}
}
cout << minx << "/" << miny << endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步