5.11 65级 Contest 题解
A.狼狼
Author: Konnyaku41377
手模一下,很显然。
第一次:\(1\times n\)
第二次:\(2\times (n-1)+1\times 2\)
第三次:\(3\times (n-2)+1\times 2+2\times2\)
……
第 $n $ 次:\(n\times (n-(n-1))+(1+2+3+4+……+n )\times 2\)
于是乎,\(O(n)\) 线性送分题,不过也有 \(O(1)\)的操作,鉴于给你们送分,不再赘述。
const int MOD=1e9+7;
ll n, ans;
int main(){
n=read();
for(ll i=1, j=n; i<=n, j>=1; ++i, --j) {
ans+=i*j;
ans%=MOD;
ans+=(i-1)*2*(n-i+1);
ans%=MOD;
}
printf("%lld", ans);
}
B.反转
Author:柳下惠
其实这个题难度放低了,数据里没有以九开头的数字,因为以九开头可能有4、5种情况,但考虑到你们的做题体验,我就给去了。
暴力 30pts
根据题意模拟,循环找到第一个反转后大于 \(n\) 的数字,那么 \(i-1\) 就是答案。
正解
首先打表。不要觉得奇怪,现在有很多题目想找到它的正解都要先打表找规律,此题也是。
打出表来,我们会发现答案都是 \(1....X\) 这种形式,其中\(.\) 代表 \(0\) 的个数,\(X\) 是 \(n\) 的最开头的数字。
再考虑两种特殊情况,一个是 \(n\leq10\) 是直接输出 \(n\),另一个是 \(n\) 类似 \(100,2000,30000.....\) 答案为 \(1...(X-1)\)。
综上,设 \(len\) 为 \(n\) 的长度,判断 \(n\) 是否能被 \(10^{len-1}\) 整除即可。
哪不明白洛谷私信问我(柳下惠)。
int t;t=read();//多测
while(t--)
{
string s;int n;bool pd=0;
n=read();s=to_string(n);
int len=s.length();
if(n%qpow(10,len-1)==0) pd=1;
if(n<=10) {
cout<<n<<"\n";
continue;
}
cout<<1;
for(int i=1;i<=len-2;i++) cout<<0;//0的个数
if(pd) cout<<(s[0]-'0')-1<<"\n";
else cout<<s[0]<<"\n";
}
C.01分树规划
Author:zcxxxxx
应 zcx 的要求挂上他的题解 Link
一道贪心题 \(qwq\)
读完题目,就知道这道题跟字符没什么关系,所以我们可以先把 * 和 ^ 看成 \(1\) 和 \(0\)。
那问题就转化成了怎么着把其中的一个序列翻转一下使其中的 \(1\) 的个数最多和最少。
---
##现在考虑一个问题,怎么求所有分数的可能性:
要知道,最小值与最大值之间的一切值都是可以得到的。
例如现在我们选择翻转一个区间 \([L, R]\),设 \(x\) 是这个区间里 \(1\) 的原始个数,\(y\) 为这个区间内 \(1\) 经过翻转可得到的最大个数。它由于 \(x\) 向 \(y\) 变化,相邻的数字相差 \(1\),所以 \(x\) 和 \(y\) 之间的所有数字必须都存在,所以就是求某区间经过翻转后总的数列中 \(1\) 的最大个数与最小个数的差值。
---
因为把 \(0\) 翻转成 \(1\) 是一个加分的操作,反之则是减分的操作,所以我们可以把 \(0\) 和 \(1\) 继续转换,分别变为 \(1\) 和 \(-1\),表示翻转该数字后加上或者减去的分数。
所以问题就转化成分别翻转哪个区间使得加分最多和减分最多喽。
接下来就是些板子了……
\(mx\) 记录翻转前边某个位置到当前位置得到的最大的 \(1\) 的个数,\(ans1\) 表示到当前位置最大的答案。
\(mn\) 与 \(ans2\) 反之。
\(x\) 表示当前位置的值
怎么求加分最多:
从头往后遍历,选择一个区间的开头,只要 \(mx + x > 0\) 就说明之前区间开头到当前位置的这个值对后边产生的答案会有贡献,所以之前选择的区间可以继续连续下去,否则的话就说明前边选择的这个区间不会对后边加分,即不会对后边答案的产生做出任何贡献,所以就没必要选择前边那些数了,改变选择区间的开头为后面一个位置……如此重复
那求减分最多你也会了吧……
遍历复杂度 \(O(N)\),复杂度很不错 \(qwq\)
可以结合一下代码:
放了一个跑的最慢的代码,可以想一下怎么优化 \(qwq\)
/**
* author: zcxxxxx
* creater: 2022.5.9
**/
#include <bits/stdc++.h>
using namespace std;
const int A = 1e2 + 7;
const int B = 1e3 + 7;
const int C = 1e4 + 7;
const int D = 3e5 + 7;
const int E = 1e6 + 7;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
inline int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int main() {
int n = read();
int ans1 = -INF, ans2 = INF, mx = 0, mn = 0, num = 0;
char ch;
for(int x, i = 1; i <= n; ++ i) {
cin >> ch;
x = (ch == '*') ? 1 : 0;
num += x;
x = (!x) ? 1 : -1;
if(mx + x > 0) mx += x;
else mx = 0;
ans1 = max(ans1, mx);
if(mn + x < 0) mn += x;
else mn = 0;
ans2 = min(ans2, mn);
}
printf("%d", ans1 - ans2 + 1);
return 0;
}
以下是我思路的代码:
#include<bits/stdc++.h>
//#pragma G++ optimize(2)
//#pragma G++ optimize(3)
//#pragma-GCC-optimize("-Ofast")
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define ushort unsigned short
#define MAX(x,y) ((x>y)?x:y)
#define MIN(x,y) ((x<y)?x:y)
#define SWAP(x,y) x^=y;y^=x;x^=y
#define I using
#define AK namespace
#define IOI std
#define ByGym_nastics return 0
const int MAXN=1e8+1;
const ll MOD=1e9+7;
I AK IOI;
ll Abs(ll a) {return a>=0?a:(~a)+1;}
ll gcd(ll a, ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a, ll b){return a/gcd(a,b)*b;}
double HAILUN(double a, double b, double c){double P=(a+b+c)/2; return sqrt(P*(P-a)*(P-b)*(P-c));}
ll Qpow(ll a, ll b) {ll base=a, re=1;while(b){if(b&1)re=(re*base)%MOD;base=(base*base)%MOD;b>>=1;}return re;}
ll Qtimes(ll a, ll b) {ll base=a, re=0;while(b){if(b&1)re=(re+base)%MOD;base=(base+base)%MOD;b>>=1;}return re;}
ll read() {ll x=0; bool f=1; char c=getchar();while(c>'9'||c<'0'){if(c=='-') f=0; c=getchar();}while(c<='9'&&c>='0')x=(x<<1)+(x<<3)+c-'0',c=getchar();return f?x:(~x)+1;}
void writ(ll x){if(x<0) {putchar('-');x=(~x)+1;}if(x>9)writ(x/10);putchar(x-x/10*10+48);}
/*void prepar(){}*/
int n;
int a[MAXN];
int Big()
{
int sum=0,Max=-0x3f;
for(int i=1;i<=n;i++)
{
sum+=a[i];
if(sum<0) sum=0;
Max=max(Max,sum);
}
return Max;
}
char ch[MAXN];
int main(){
n=read();
scanf(" %s",ch+1);
for(int i=1;i<=n;i++) a[i]=(ch[i]=='^')?1:-1;
int res1=Big();
for(int i=1;i<=n;i++) a[i]=-a[i];
int res2=Big();
writ(res1+res2+1);
ByGym_nastics;
/*ACdate:2022.5.10*/
}
D.牛的数组
Author:双枫浦
\(n^2\) 的做法
考虑按照题意进行模拟
要求最后的数组为有序的且要按数组给定的顺序对 \(x\) 进行操作,则每一个新加入牛数组某一端的 \(x\) 必须保证普通数组中没有与当前牛数组这一端的那个数之间的数,否则就要新建一个牛数组
比如样例中
6
3 6 0 9 5 4
新建一个牛数组将\(3\)放入 { \(3\) }
那么下一个考虑把 \(6\) 放入上一个牛数组的后端,即 { \(3,6\) }
但是普通数组中包括 \(4\) ,若按上述做法则无法最终构成一个有序的序列
因此只能新开一个牛数组将 \(6\) 放入 { \(6\) }
重复上述步骤即可拿到 \(60\) 分
struct op{
int head,tail;
}e[M];
int tot,a[M],ans;
int main() {
int n; cin>>n;
for(int i = 1;i <= n;++ i) a[i] = read();
e[++tot] = op{a[1],a[1]}; ans++;
for(int i = 2;i <= n;++ i) {
int bj = 0;
for(int j = 1;j <= tot;++ j) {
int pd = 0;
if(a[i]>e[j].tail) {
for(int k = i+1;k <= n;++ k)
if(a[k]>e[j].tail&&a[k]<a[i]) pd = 1;
if(!pd) {
e[j].tail = a[i]; bj = 1;
break;
}
}
else if(a[i]<e[j].head) {
for(int k = i+1;k <= n;++ k)
if(a[k]<e[j].head&&a[k]>a[i]) pd = 1;
if(!pd) {
e[j].head = a[i]; bj = 1;
break;
}
}
}
if(!bj) e[++tot] = op{a[i],a[i]};
}
cout<<tot;
return 0;
}
O(nlogn)
因为只能从前往后依次进入队列,然后我们还要让这些队列总的是有序的,所以我们先排个序,然后扫原数组,每次 lower_bound 查找比当前这个数排名大 \(1\) 或者小 \(1\) 的数,如果那个数在队列里,我们就入队,否则就要新开一个队列,因为队列中的数排名是依次递增的,必须紧挨着,这样我们就得到了 \(O(n~logn)\) 的做法
int n;
int sz[100005], a[100005];
int ans;
map<int, bool>pd;//直接判断这个数有没有出现过,常数很大,map自动离散化
int main(){
n=read(); ans=n;
for(int i=1; i<=n; ++i) sz[i]=read(), sz[i], a[i]=sz[i];
sort(a+1, a+n+1);
int ans=1; pd[sz[1]]=1;
for(int i=2; i<=n; ++i) {
int k1=*upper_bound(a+1, a+n+1, sz[i]);
int k2=*(lower_bound(a+1, a+n+1, sz[i])-1);
if(pd[k1]||pd[k2]) {//能放就放
pd[sz[i]]=1;
} else {//否则新开
pd[sz[i]]=1;
++ans;
}
}
printf("%d", ans);
}
E.天堂之路
Author:Gym_nastics
大原题,事出于今上午龙突然要求出题,因为意识到没有树的题,于是众人苦想,最终我想到了这一题。
原题是:P2052 [NOI2011] 道路修建
在 柳下惠爬虫的加持下搞到了题,那个丢掉的 1 应该是爬掉了(,TLE_Automation 修改题面。
没时间了,一句话题解:dfs预处理出子树大小和深度,按题意求和即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2000010;
int tot=0;
int n;
int fr[maxn],to[maxn],dis[maxn],head[maxn];;
int siz[maxn];
int dep[maxn];
int RT,len;
long long ans;
struct edge {
int u,v,dis,nxt;
} e[maxn];
void add(int u, int v,int dis) {
++tot;
e[tot].u=u;
e[tot].v=v;
e[tot].dis=dis;
e[tot].nxt=head[u];
head[u]=tot;
}
void dfs(int u,int fat) {
dep[u]=dep[fat]+1;
siz[u]=1;
for(int i=head[u]; i; i=e[i].nxt) {
int to=e[i].v;
if(to==fat) continue;
dfs(to,u);
siz[u]+=siz[to];
}
}
signed main() {
cin>>n;
for(int i=1; i<n; i++) {
cin>>fr[i]>>to[i]>>dis[i];
add(fr[i],to[i],dis[i]);
add(to[i],fr[i],dis[i]);
}
dfs(1,0);
for(int i=1; i<n; i++) {
if(dep[fr[i]]>dep[to[i]]) swap(fr[i],to[i]);
ans+=dis[i]*abs(siz[1]-siz[to[i]]-siz[to[i]]);
}
cout<<ans;
}
这是我去年暑假期间的代码,不代表我真实马蜂(((
F.点星辰
Author:TLE_Automation
首先这个四元组是否合法显然只和 \(ak^3\) 有关。
那么不妨二分这个 \(S\),对于 check 函数就枚举 \(k\),对于每个 \(k\),只有 \([\frac{S}{k^3}](向下取整)\)个。
有一个问题就是这个二分比较奇怪,可能有多个 \(S\) 满足 \(Check(S) = n\)。我们只用找出最小的。
那就把条件改一下,找到最大的满足 \(Check(S) < n\),这样最后二分出的答案 \(+1\) 就是 \(n\) 了。
时间复杂度 \(\mathcal{O}(T \cdot n^{\frac{1}{3}}logn)\)
/*
Work by: TLE_Automation
*/
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
#define int long long
using namespace std;
const int N = 1e6 + 10;
const int MAXN = 2e5 + 10;
inline char readchar(){
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int read() {
int res = 0, f = 0;
char ch = readchar();
for(; !isdigit(ch); ch = readchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = readchar()) res = (res << 1) + (res << 3) + (ch ^ '0');
return f ? -res : res;
}
inline void print(int x) {
if (x < 0 ) putchar('-'), x = -x;
if (x > 9 ) print(x / 10);
putchar(x % 10 + '0');
}
int n;
int Check(int x) {
int res = 0;
for(register int i = 2; i * i * i <= x; i++) res += x / (i * i * i );
return res;
}
int ans = 0, l, r, mid;
signed main() {
int T = read();
while(T--) {
n = read();
ans = 0, l = 1, r = 4949100894494448;
while(l <= r) {
mid = (l + r) >> 1;
if(Check(mid) < n) ans = mid, l = mid + 1;
else r = mid - 1;
}
if(Check(ans + 1) == n) printf("%lld\n", ans + 1);
else printf("-1\n");
} return 0;
}
因为直接copy下来的代码放在博客园里马蜂会变得奇奇怪怪,所以以上代码不代表真实马蜂/qd