来自蒟蒻的 锐 钝评:三道签到题:A,C,F(评价为水中水);稍有思考:E(赛场上想到了不知为何写WA了);没想到:B,D,G。G题更是重量级,细节要比其他题多不少
部分idea来自我的队友zhuge0和laonongmin,我一人是补不完的(悲)
A
题意:给出一串身份证码,提取特定位数判断生日是否为合理日期
直接模拟注意闰年判断即可
B
题意:给出长度为n的数组,询问所有区间里\(\max(a_l,a_{l+1},...,a_r)\times\min(a_l,a_{l+1},...,a_r)\times(r-l+1)\)的最大值
首先确定枚举对象,由于当某个区间的最小值确定后,只需让其左右区间尽可能的向大扩大即可,故可以枚举以a[i]为最小值的最大区间左右端点和区间内最大值
附上ac代码,采用了构建笛卡尔树求区间大小,线段树查询区间最值,用__int128来完成高精运算。
其实完全没必要,直接用单调栈求以上所有值,再用python写高精,大概十行写完。因为想练一下数据结构 博主太懒 后面再补吧~~
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6+10;
int a[N],l[N],r[N],f[N],l_sz[N],r_sz[N];
int n;
#define int __int128
inline void read(int &n){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
n=x*f;
}
inline void print(int n){
if(n<0){
putchar('-');
n*=-1;
}
if(n>9) print(n/10);
putchar(n % 10 + '0');
//cout<<'\n';
}
#undef int
struct node
{
int maxn,minn;
int l,r;
}tr[N<<2];
void pushup(int p)
{
tr[p].maxn=max(tr[p<<1].maxn,tr[p<<1|1].maxn);
tr[p].minn=min(tr[p<<1].minn,tr[p<<1|1].minn);
}
void build(int l,int r,int p)
{
tr[p].l=l,tr[p].r=r;
if(l==r)
{
tr[p].maxn=tr[p].minn=a[l];
return ;
}
int m=(l+r)>>1;
build(l,m,p<<1);
build(m+1,r,p<<1|1);
pushup(p);
}
int query_1(int l,int r,int p,int x,int y)
{
if(l>y||r<x) return 0;
if(x<=l&&y>=r) return tr[p].maxn;
int m=(l+r)>>1;
return max(query_1(l,m,p<<1,x,y),query_1(m+1,r,p<<1|1,x,y));
}
int dfs(int x)
{
if(~f[x]) return f[x];
l_sz[x]=0;r_sz[x]=0;
if(l[x]) {l_sz[x]++;l_sz[x]+=dfs(l[x]);}
if(r[x]) {r_sz[x]++;r_sz[x]+=dfs(r[x]);}
return f[x] = l_sz[x]+r_sz[x];
}
void build()
{
int root = 0;
stack<int> stk;
for(int i=1;i<=n;++i)
{
int last = 0;
while(stk.size()&&a[i]<a[stk.top()])
{
last = stk.top();
stk.pop();
}
if(!stk.empty()) r[stk.top()] = i;
else root = i;
l[i] = last;
stk.push(i);
}
//for(int i=1;i<=n;++i) cout<<l[i]<<' '<<r[i]<<'\n';
}
void solve()
{
memset(f,-1,sizeof f);
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
build(); //构建笛卡尔树
build(1,n,1); //构建线段树
__int128 ans = 0;
for(int i=1;i<=n;++i)
{
dfs(i); //查询i点子树的大小
__int128 L = i-l_sz[i], R = i+r_sz[i];
__int128 minn = a[i],maxn = query_1(1,n,1,L,R);
ans = max(ans,minn*maxn*(R-L+1));
}
print(ans);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
}
C
题意:n+m个均匀分布的随机变量,最大值出现在前n个的概率
嗯~~,直接输出n\(\div (n+m)\) 即可
D
题意:一个长度为n的01串,规定当全为1或再连续的长为x的0串后接上长度至少为x的1串,每一位出现0的概率为p,1的概率为1-p,求该字符串为合法字符串的概率
首先根据概率DP的基本思想,\(f[k] = p\times f[k-1] + \sum_{i=1}^{\lfloor {k\div 2}\rfloor} p^i\times (1-p)^i\times f[k-2i]\)
状态转移:若第k位为1且前k-1位为一个合法串,概率为\(p\times f[k-1]\),否则若前k-1位不是合法串,且能通过构造变成合法串共有\(\lfloor {k\div 2}\rfloor\)种情况,直接将概率加和即可
在此发现较难维护的是后者,可以用\(g[k] = \sum_{i=1}^{\lfloor {k\div 2}\rfloor} p^i\times (1-p)^i\times f[k-2i]\) 维护g数组
g数组的转移推导:\(p\times (1-p)\times g[k-2] = \sum_{i=2}^{\lfloor {k\div 2}\rfloor} p^i\times (1-p)^i\times f[k-2i]\)
可以发现二者差值为 \(p\times(1-p)\times f[k-2]\)
则有\(g[k] = p\times (1-p)\times (g[k-2] + f[k-2])\)
附上ac代码
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int,int> PII;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<string> VS;
typedef vector<int> VI;
typedef vector<vector<int>> VVI;
const int mod = 998244353;
//求快速幂和逆元
ll quick_pow(ll a,int b)
{
a%=mod;
ll ans=1;
for(;b;b>>=1)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
}
return ans;
}
ll inv(ll x)
{
return quick_pow(x,mod-2);
}
void solve()
{
int n,p;
cin>>n>>p;
ll P = p*inv(100)%mod,NP = (100-p)*inv(100)%mod;
vector<ll> f(n+1),g(n+1);
f[0] = 1, f[1] = P, f[2] = (P*f[1] + NP*P)%mod;
g[1] = 0, g[2] = P*NP%mod;
for(int i=3;i<=n;++i)
{
//注意此处P*NP后需要先取mod继续乘
g[i] = P*NP%mod*(g[i-2]+f[i-2])%mod;
f[i] = (P*f[i-1] + g[i])%mod;
}
cout << f[n] << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
}
E
题意:n棵树环形排列,每天每颗树生长高度为上一棵树的高度,即\(a[i] = a[i] + a[i-1]\),有q次查询求每次查询区间里有多少棵树大于查询值hi(hi<=1e18)
显然整体上指数倍增,只需模拟不超过63次,然后每次都输出区间长度即可,但直接用py模拟63次会超时,可能是博主太菜 在此加上一个标记看有多少数大于1e18,当标记值==n后直接输出区间长度,可以加速模拟(似乎不能稳定过TLE,不过懒得改了)
附上ac代码
n, q = map(int, input().split())
INF = 1e18
a = [0] * n
a[0:n] = list(map(int, input().split()))
num = 0
for i in range(0, q):
l, r, h = map(int, input().split())
l -= 1
r -= 1
if num >= n:
print(r - l + 1)
else:
cnt = 0
tmp = a[n - 1]
num = 0
for j in range(n - 1, 0, -1):
if a[j] <= INF:
a[j] += a[j - 1]
if j >= l and j <= r and a[j] > h:
cnt += 1
if a[j] > INF:
num += 1
a[0] += tmp
if 0 >= l and a[0] > h:
cnt += 1
if a[0] > INF:
num += 1
print(cnt)
F
题意:给你一个 \(10^{1000}\) 以内的数,问是否是 3 的倍数。
显然读入一下输出答案即可
G(个人认为本场最难写的题)
题意:有一个 n × m 的网格,可能为陆地或水域。每个网格上有若干游客。只能四联通走陆地,水域不能行走。有 k 个旅游景点,影响力在初始坐标中为 pi,每走一步,影响力减一,最多减到 0。对于一个格子,若有唯一一个影响力最大的景点,那么这个格子的游客都将前往这个景点。问最终每个景点的游客数量。n, m ≤ 500,pi ≤ 106。
- 第一想法从k个景区出发bfs显然是不行的,然后想优化
其实是看题解本题类似flood fill(只是借用下名字),将每个景点视作一个源点,其流量会随着距离减小至0,那么可以采取优先队列的bfs,让每个点被更新时就得到其最大流量值,以次来减少点的更新次数* - 具体实现上用val[i][j]表示其目前最大流量值,ne[i][j]表示其源头,vis[i][j]表示是否为合法点(即至多有一个流量最大的源头)*
- 在优先队列里若pi(即当前节点的流量)-1 > val[i][j](即下一步走向的节点)就更新val[i][j],并把(i,j)放入队列,否则若pi==val[i][j]且id(即当前节点源头的标号)与ne[i][j]不同,说明有至少两个源头该点是非法点,不再加入队列*
- 但上面的思路还有问题,即使id == ne[i][j]也可能是非法点,因为当前节点就可能是非法点,所以即使当前节点和(i,j)有着相同的源头,(i,j)也可能为非法点。加上一个判断就能过了*
附上ac代码
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int,int> PII;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<string> VS;
typedef vector<int> VI;
typedef vector<vector<int>> VVI;
const int N = 510;
int t[N][N],vis[N][N];
int val[N][N],ne[N][N];
char g[N][N];
int ans[N*N];
int dir[4][2]={1,0,-1,0,0,1,0,-1};
struct node
{
int x,y,pi,id;
//x,y记录当前点的坐标,pi表示吸引值,id表示从哪个点转移的
bool operator < (const node& T) const
{
return pi < T.pi;
}
};
void solve()
{
int n,m;
cin>>n>>m;
ll sum = 0;
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
cin>>g[i][j];
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
{
cin>>t[i][j];
sum+=t[i][j];
}
int k;
cin>>k;
priority_queue<node> q;
/*vector<node> pot(k);*/
for(int i=0;i<k;++i)
{
int x,y,pi;
cin>>pi>>x>>y;
x--;y--;
if(pi==0) continue;
q.push({x,y,pi,i});
val[x][y] = pi,ne[x][y] = i,vis[x][y] = 1;
}
//特判只有一个景区的情况
if(k==1) {cout<<sum<<'\n';return ;}
while(q.size())
{
auto [x,y,pi,id]=q.top();q.pop();
for(int i=0;i<4;++i)
{
int xx = x+dir[i][0];
int yy = y+dir[i][1];
//pi-1不能为0
if(xx>=0&&xx<n&&yy>=0&&yy<m&&pi-1>=1&&g[xx][yy]=='*')
{
if(pi-1>val[xx][yy])
{
vis[xx][yy] = vis[x][y];
val[xx][yy] = pi-1;
ne[xx][yy] = id;
q.push({xx,yy,pi-1,id});
}
else if(pi-1==val[xx][yy])
{
if (id != ne[xx][yy])
vis[xx][yy] = -1;
else if (vis[x][y] == -1)
vis[xx][yy] = -1;
}
}
}
}
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
{
if(vis[i][j]==1)
{
ans[ne[i][j]]+=t[i][j];
}
}
for(int i=0;i<k;++i) cout<<ans[i]<<" \n"[i==k-1];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
}
posted on
浙公网安备 33010602011771号