带更。
- B. Beautiful Sequence Unraveling
- C. Brave Seekers of Unicorns
- D. Bank Security Unification
- E. Brief Statements Union
- F. Border Similarity Undertaking
- G. Biological Software Utilities
- H. Bytelandia States Union
- I. Binary Supersonic Utahraptors
- J. Burnished Security Updates
- K. Bookcase Solidity United
- M. Brilliant Sequence of Umbrellas
- N. Best Solution Unknown
感谢 @hht2005 的帮助。
B. Beautiful Sequence Unraveling
定义一个长度为 (n) 好序列 (a) 是不存在一个位置 (1le i<n) 使得 (max{a_1,ldots,a_i}=min{a_{i+1},ldots,a_n}) 的序列。
对长度为 (n),值域为 ([1,k]) 的好序列计数,对 (p) 取模并输出。
(1le nle 400),(1le kle 10^8),(998244353le ple 10^9+9),(pin ext{prime})。
Solution
(k) 是坑你的,如果考虑和 (k) 有关的直接凉凉。
考虑回避 (k),不难发现合法的序列出现的不同的数字只有 (n) 种,直接离散化。
考虑 (f_{i,j}) 表示一个长度为 (i) 的序列,值域为 ([1,j]) 的好序列个数。
好序列并不好求,但考虑一个不好序列 (a),设其最大的位置 (p) 使得 (max{a_1,ldots,a_p}=min{a_{p+1},ldots,a_n}),容易发现一个大性质:(a_{p+1},ldots,a_n) 是一个好序列,如果其不是一个好序列的话,(p) 便不是最大的。
枚举最大的位置 (p),枚举其值 (m) 可以得到 DP 式子:
使用前缀和优化可以做到 (O(n^3))。
考虑再设 (g_i) 表示长度为 (n) 的序列,值域为 ([1,i]) 的好序列个数:
所以答案为:(displaystyle sum^n_{i=1}inom k i g_i)。
组合数暴力计算,时间复杂度 (O(n^3))。
Code
const int N=400;
int n,K,mod,f[N+10][N+10],ans,g[N+10],sum[N+10][N+10][N+10];
void Add(int &x,int y) {
x+=y;
if(x>=mod) x-=mod;
}
void Del(int &x,int y) {
x-=y;
if(x<0) x+=mod;
}
int fpow(int x,int y) {
int ret=1;
for(;y;y>>=1) {
if(y&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod;
}
return ret;
}
int C(int n,int m) {
int ret=1,fm=1;
for(int i=n;i>=n-m+1;i--) ret=1ll*ret*i%mod;
for(int i=1;i<=m;i++) fm=1ll*fm*i%mod;
return 1ll*ret*fpow(fm,mod-2)%mod;
}
//n!/(n-m)!/m!
int po[N+10][N+10],inv[N+10][N+10];
void init() {
for(int i=1;i<=n;i++) {
po[i][0]=inv[i][0]=1;
inv[i][1]=fpow(po[i][1]=i,mod-2);
for(int j=2;j<=n;j++) po[i][j]=1ll*po[i][j-1]*i%mod,inv[i][j]=1ll*inv[i][j-1]*inv[i][1]%mod;
}
}
int main() {
n=read(),K=read(),mod=read();
init();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) {
f[i][j]=po[j][i];
for(int t=1;t<=j;t++) {
int A=po[t][i],B=po[t-1][i];
int sum1=(sum[i-1][j-t][t]-sum[i-1][j-t+1][t]+mod)%mod;
int sum2=(sum[i-1][j-t+1][t-1]-sum[i-1][j-t][t-1]+mod)%mod;
Add(f[i][j],(1ll*A*sum1%mod+1ll*B*sum2%mod)%mod);
}
for(int t=1;t<=n;t++) sum[i][j][t]=(sum[i-1][j][t]+1ll*f[i][j]*inv[t][i]%mod)%mod;
}
for(int i=1;i<=n;i++) {
g[i]=f[n][i];
for(int j=1;j<i;j++) Del(g[i],1ll*C(i,j)*g[j]%mod);
}
for(int i=1;i<=n;i++) Add(ans,1ll*C(K,i)*g[i]%mod);
write((ans+mod)%mod),enter;
return WDNMD;
}
C. Brave Seekers of Unicorns
定义一个好序列满足如下条件:
- 序列非空。
- 没有三个连续的元素异或和 (=0)。
- 序列递增。
- 序列的值域为 ([1,n])。
给定 (n),对好序列计数,答案对 (998244353) 取模。
Solution
设 (f_i) 表示以 (i) 结尾的所有方案数,有:
(f_j) 可以一波前缀和带走,考虑后面,可以变形为 (sum_{k<koplus i<i} f_k)。
因为要考虑 (ioplus joplus k=0) 的情况,所以一个位上最多只有两个 (1),枚举二进制上 (i) 的除最高位的 (1) 位 (d),钦定 (j) 的第 (d) 位是 (1),(k) 的第 (d) 位为 (0),那么第 (d) 位往后都可以随便乱取。
那么 (k) 的范围就是 ([2^d,2^{d+1}-1]),也可以前缀和了。
时间复杂度 (O(nlog n))。
Code
const int N=1e6,mod=998244353;
ll f[N+10],n,sum[N+10];
int main() {
scanf("%d",&n);
f[1]=1;sum[1]=1;
for(int i=2;i<=n;i++) {
f[i]=sum[i-1]+1;
int j;
for(j=20;j>=0;j--)
if(i&(1<<j)) break;
j--;
for(;j>=0;j--) if(i&(1<<j)) f[i]=(f[i]-sum[(1<<(j+1))-1]+sum[(1<<j)-1]+mod)%mod;
sum[i]=(f[i]+sum[i-1])%mod;
}
printf("%lld
",sum[n]%mod);
}
D. Bank Security Unification
从一个长度为 (n) 序列 (a) 中取出一个子序列,使得相邻两个元素的按位与值的和最大。
(2le nle 10^6),(0le a_ile 10^{12})。
Solution
我们希望按位与和最大,就是在希望相邻两个元素的二进制位数尽可能相同。
考虑 (f_i) 表示末位为 (i) 的子序列答案最大是多少,朴素转移是 (O(n^2)) 的。
考虑剪枝,如果考虑当前的一位,我们想从哪里转移而来,显然是与之相同的位,如果选这些位靠后一定较靠前的优秀。
所以记录 (las_{i,0/1}) 表示第 (i) 位上一个是 (0/1) 在哪里,每次只从对应的 (las) 转移而来。
时间复杂度 (O(nlog a_i))。
Code
const int N=1e6;
int n;
ll f[N+10],g[N+10],ans;
int las[44][2];
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&f[i]);
g[2]=(f[1]&f[2]);
for(int i=1;i<=2;i++)
for(int j=39;j>=0;j--)
if(f[i]&(1ll<<j)) las[j][1]=i;
else las[j][0]=i;
for(int i=3;i<=n;i++) {
for(int j=39;j>=0;j--) {
int zz=(bool)(f[i]&(1ll<<j));
g[i]=max(g[i],g[las[j][zz]]+(f[las[j][zz]]&f[i]));
}
for(int j=39;j>=0;j--)
if(f[i]&(1ll<<j)) las[j][1]=i;
else las[j][0]=i;
}
for(int i=1;i<=n;i++) ans=max(ans,g[i]);
printf("%lld
",ans);
}
E. Brief Statements Union
给定 (n) 个数字,(k) 条限制,限制形如 (a_lAnd a_{l+1}AndldotsAnd a_{r}=x)。
对于某一条限制,如果可以删去这条限制,使得存在满足条件的序列,称这是好的。
输出哪些序列是好的,哪些序列是坏的。
(1le n,kle 10^6),(0le x_ile 10^{18})。
Solution
按位考虑,那么剩下的限制分为两类:(1) 限制和 (0) 限制。
覆盖掉 (1) 限制,考虑 (0) 限制的冲突个数:
- 为 (0):全都可以。
- 为 (1):那个冲突的是内鬼,刀了。
- (>1):咋整都不行。
(0) 限制的情况就考虑完了。
考虑如何删除 (1) 限制,对于每一个只被 (1) 限制覆盖一次的点,我们可以删除这个 (1) 限制,来满足若干 (0) 限制。
对于某一个 (0) 限制,我们将能够删除的 (1) 限制,使得该 (0) 限制合法化的 (1) 限制取出,对所有 (0) 限制的如此集合取交。
运用差分可以做到 (O(nlog x_i))。
Code
const int N=1e6;
int n,K;
int ql[N+10],qr[N+10];
ll qx[N+10],f[N+10],g[N+10];
int nxt[N+10],pre[N+10],ans[N+10];
int vio[N+10],tot,id,pos[N+10],seg[N+10];
int main() {
scanf("%d %d",&n,&K);
for(int i=1;i<=K;i++) scanf("%d %d %lld",&ql[i],&qr[i],&qx[i]);
nxt[n+1]=n+1,pre[0]=0;
for(int k=0;k<60;k++) {
for(int i=1;i<=n;i++) f[i]=g[i]=0;
for(int i=1;i<=K;i++)
if((1ll<<k)&qx[i])
f[ql[i]]++,f[qr[i]+1]--,
g[ql[i]]+=i,g[qr[i]+1]-=i;
for(int i=1;i<=n;i++) f[i]+=f[i-1],g[i]+=g[i-1];
for(int i=n;i>=1;i--) nxt[i]=f[i]?nxt[i+1]:i;
id=tot=0;
for(int i=1;i<=K;i++)
if(!((1ll<<k)&qx[i]))
if(nxt[ql[i]]>qr[i])
vio[++tot]=i;
if(tot==0) {
for(int i=1;i<=K;i++) ans[i]++;
continue;
}
if(tot==1) ans[vio[1]]++;
for(int i=1;i<=n;i++)
if(f[i]==1&&!pos[g[i]])
seg[pos[g[i]]=++id]=g[i];
for(int i=1;i<=n;i++) pre[i]=(f[i]==1)?i:pre[i-1];
for(int i=n;i>=1;i--) nxt[i]=(f[i]==1)?i:nxt[i+1];
int l=0,r=id;
for(int i=1;i<=tot;i++) {
int u=vio[i];
if(nxt[ql[u]]<=qr[u]) l=max(l,pos[g[nxt[ql[u]]]]);
else l=id+1;
if(pre[qr[u]]>=ql[u]) r=min(r,pos[g[pre[qr[u]]]]);
else r=0;
}
for(int i=l;i<=r;i++) ans[seg[i]]++;
for(int i=1;i<=K;i++) pos[i]=0;
}
for(int i=1;i<=K;i++)
if(ans[i]==60) putchar('1');
else putchar('0');
puts("");
}
F. Border Similarity Undertaking
给定一个字母矩形,求出其有多少的子矩形满足四周都是相同字符。
(1le n,mle 2000)。
Solution
考虑矩阵分治,即对一个矩阵的长宽进行分治。
对于每一个矩阵进行分治,考虑计算跨过中心线的矩阵个数。
这个过程可以预处理出一个点向上,向下,向左,向右的延伸距离,可以暴力处理。
中心线左右两边分别处理,处理出左右两边能组成的矩形数。
代码细节较多较复杂,时间复杂度 (O(nmlog n))。
Code
const int N=2000;
int n,m;
char ch[N+10][N+10];
int up[N+10][N+10],dn[N+10][N+10],lef[N+10][N+10],rig[N+10][N+10];
int al[N+10][N+10],ar[N+10][N+10];
ll ans;
void solve(int l1,int r1,int l2,int r2) {
if(l1==r1||l2==r2) return;
if(r1-l1+1<=r2-l2+1) {
int mid=(r2+l2)>>1,L,R;
for(int i=l1;i<=r1;i++)
for(int j=l1;j<=r1;j++)
al[i][j]=ar[i][j]=0;
for(int i=l1;i<=r1;i++) {
for(int j=mid;j>=l2&&j>=lef[i][mid];j--)
al[i][min(dn[i][j],r1)]++,
al[i][max(up[i][j],l1)]++;
for(int j=r1-1;j>=i;j--) al[i][j]+=al[i][j+1];
for(int j=l1+1;j<=i;j++) al[i][j]+=al[i][j-1];
for(int j=mid+1;j<=r2&&j<=rig[i][mid+1];j++)
ar[i][min(dn[i][j],r1)]++,
ar[i][max(up[i][j],l1)]++;
for(int j=r1-1;j>=i;j--) ar[i][j]+=ar[i][j+1];
for(int j=l1+1;j<=i;j++) ar[i][j]+=ar[i][j-1];
}
for(int i=l1;i<=r1;i++)
for(int j=i+1;j<=r1;j++) {
if(ch[i][mid]!=ch[j][mid]) continue;
if(ch[i][mid]!=ch[i][mid+1]) continue;
if(ch[j][mid]!=ch[j][mid+1]) continue;
L=lef[i][mid]>=lef[j][mid]?al[i][j]:al[j][i];
R=rig[i][mid+1]<=rig[j][mid+1]?ar[i][j]:ar[j][i];
ans+=1ll*L*R;
}
solve(l1,r1,l2,mid);
solve(l1,r1,mid+1,r2);
}
else {
int mid=(l1+r1)>>1,L,R;
for(int i=l2;i<=r2;i++)
for(int j=l2;j<=r2;j++)
al[i][j]=ar[i][j]=0;
for(int i=l2;i<=r2;i++) {
for(int j=mid;j>=l1&&j>=up[mid][i];j--)
al[i][min(rig[j][i],r2)]++,
al[i][max(lef[j][i],l2)]++;
for(int j=r2-1;j>=i;j--) al[i][j]+=al[i][j+1];
for(int j=l2+1;j<=i;j++) al[i][j]+=al[i][j-1];
for(int j=mid+1;j<=r1&&j<=dn[mid+1][i];j++)
ar[i][min(rig[j][i],r2)]++,
ar[i][max(lef[j][i],l2)]++;
for(int j=r2-1;j>=i;j--) ar[i][j]+=ar[i][j+1];
for(int j=l2+1;j<=i;j++) ar[i][j]+=ar[i][j-1];
}
for(int i=l2;i<=r2;i++)
for(int j=i+1;j<=r2;j++) {
if(ch[mid][i]!=ch[mid][j]) continue;
if(ch[mid][i]!=ch[mid+1][i]) continue;
if(ch[mid][j]!=ch[mid+1][j]) continue;
L=up[mid][i]>=up[mid][j]?al[i][j]:al[j][i];
R=dn[mid][i]<=dn[mid][j]?ar[i][j]:ar[j][i];
ans+=1ll*L*R;
}
solve(l1,mid,l2,r2);
solve(mid+1,r1,l2,r2);
}
}
int main() {
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>ch[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) {
up[i][j]=i>1&&ch[i][j]==ch[i-1][j]?up[i-1][j]:i;
lef[i][j]=j>1&&ch[i][j]==ch[i][j-1]?lef[i][j-1]:j;
}
for(int i=n;i>=1;i--)
for(int j=m;j>=1;j--) {
dn[i][j]=i<n&&ch[i][j]==ch[i+1][j]?dn[i+1][j]:i;
rig[i][j]=j<m&&ch[i][j]==ch[i][j+1]?rig[i][j+1]:j;
}
solve(1,n,1,m);
printf("%lld
",ans);
}
G. Biological Software Utilities
求出存在完美匹配的 (n) 个点的树的数量。(1le nle 10^6)。
Solution
把两个点绑一块,共有 (n!!=1 imes 3 imes ldots imes (n-1)) 种方法。
绑一块的点能组成的树,有 ((frac{n}{2})^{n/2-2}) 种。
点与点之间相互连接,有 (4^{n/2-1}) 种。
乘起来,没了,时间复杂度 (O(n))。
Code
const int N=1e6,mod=998244353;
int n;
int ans=1;
int fpow(int x,int y) {
if(y<0) return 1;
int ret=1;
for(;y;y>>=1) {
if(y&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod;
}
return ret;
}
int main() {
scanf("%d",&n);
if(n%2) {puts("0");return 0;}
for(int i=1;i<=n;i+=2) ans=1ll*ans*i%mod;
ans=1ll*ans*fpow(n/2,n/2-2)%mod*fpow(4,n/2-1)%mod;
printf("%d
",ans);
}
H. Bytelandia States Union
从 ((x_1,y_1)) 走到 ((x_2,y_2)),求最短路径。
其中从 ((x,y)) 向四个方向走的方案不一,具体看原题。
(1le Tle 5 imes 10^4),(1le x_1,y_1,x_2,y_2le 10^9)。
Solution
诈骗题,我可以很负责任的告诉你,一条路径 ((x_1,y_1),(x_2,y_2),(x_3,y_3),ldots,(x_k,y_k)) 的费用是 (x^2_ky^2_k-x^2_1y^2_1-x_k^2-x_k^2+sum^{k}_{i=1}x^2_i+y^2_i),证明?可以问hht2005,总之我不会。
最小化路径,只需最小化后面的值,所以只要尽可能挨近 (x=y) 这条直线就可以了。
大力分讨即可,单次时间复杂度 (O(1))。
Code
void solve() {
int x1,y1,x2,y2;
scanf("%lld %lld %lld %lld",&x1,&y1,&x2,&y2);
ans=(p2(1ll*x2*y2%mod)-p2(1ll*x1*y1%mod)+mod)%mod;
ans=(ans-p2(x2)-p2(y2)+mod)%mod;
if(x1>x2) swap(x1,x2),swap(y1,y2);
if(y2>y1)
if(x1<y1) {
int nx=min(y1,x2);
Add(ans,1ll*(nx-x1)*p2(y1)%mod);
Add(ans,sum(x1,nx-1));
if(nx==x2) {
Add(ans,1ll*(y2-y1+1)*p2(x2)%mod);
Add(ans,sum(y1,y2));
}
else {
int nxt=min(x2,y2);
Add(ans,(sum(nx,nxt-1)*3ll%mod+sum(nx+1,nxt))%mod);
if(nxt==y2) {
Add(ans,1ll*p2(y2)*(x2-nxt+1)%mod);
Add(ans,sum(nxt,x2));
}
else {
Add(ans,1ll*p2(x2)*(y2-nxt+1)%mod);
Add(ans,sum(nxt,y2));
}
}
}
else {
int ny=min(x1,y2);
Add(ans,1ll*(ny-y1)*p2(x1)%mod);
Add(ans,sum(y1,ny-1));
if(ny==y2) {
Add(ans,1ll*(x2-x1+1)*p2(y2)%mod);
Add(ans,sum(x1,x2));
}
else {
int nxt=min(x2,y2);
Add(ans,sum(ny,nxt-1)*2ll%mod);
Add(ans,sum(ny,nxt-1)+sum(ny+1,nxt));
if(nxt==x2) {
Add(ans,1ll*p2(x2)*(y2-nxt+1)%mod);
Add(ans,sum(nxt,y2));
}
else {
Add(ans,1ll*p2(y2)*(x2-nxt+1)%mod);
Add(ans,sum(nxt,x2));
}
}
}
else {
Add(ans,1ll*(y1-y2)*p2(x1)%mod);
Add(ans,sum(y2+1,y1));
Add(ans,1ll*(x2-x1+1)*p2(y2)%mod);
Add(ans,sum(x1,x2));
}
printf("%lld
",(ans%mod+mod)%mod);
}
signed main() {
ll t;scanf("%lld",&t);
while(t--) solve();
}
I. Binary Supersonic Utahraptors
Alice 和 Bob 又在玩游戏,Alice 有黑与白两种物品,Bob 也有黑与白两种物品。
有 (k) 个回合,每一回合中,Alice 先送给 Bob (s_i) 个物品,Bob 则回礼 (s_i) 个物品。
Alice 想让 Alice 的黑物品与 Bob 的白物品差尽可能小,Bob 则想让上述东西最大。
求最后的这个值。
(1le n,m,kle 3 imes 10^5)。
Solution
人定不胜天,Alice 和 Bob 并不能决定这一切。
答案恒定不变,是总的黑物品数与 (n) 的差。
时间复杂度 (O(1))。
Code
#include<bits/stdc++.h>
using namespace std;
int n,m,k,r,y;
int main() {
scanf("%d %d %d",&n,&m,&k);
for(int i=1,x;i<=n;i++) {
scanf("%d",&x);
if(x==1) r++;
}
for(int i=1,x;i<=m;i++) {
scanf("%d",&x);
if(x==1) r++;
}
printf("%d
",abs(n-r));
}
J. Burnished Security Updates
求一张图的最小的覆盖所有边的独立集,(2le nle 3 imes 10^5),(1le mle 3 imes 10^5)。
Solution
要覆盖所有边,所以相邻的两个点一定取得状态不同。
考虑二分图染色,如果原图有奇环,则一定无解,否则取较小的那种颜色的点集。
时间复杂度 (O(n+m))。
Code
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=3e5;
int n,m,vis[N+10],cnt[4],ans;
vector<int> G[N+10];
bool dfs(int u,int col) {
vis[u]=col;cnt[col]++;
bool fl=1;
for(auto v:G[u])
if(!vis[v]) fl&=dfs(v,3-col);
else if(vis[v]==col) return 0;
return 1;
}
int main() {
scanf("%d %d",&n,&m);
for(int i=1,x,y;i<=m;i++) {
scanf("%d %d",&x,&y);
G[x].pb(y),G[y].pb(x);
}
for(int i=1;i<=n;i++)
if(!vis[i]) {
cnt[1]=cnt[2]=0;
if(!dfs(i,1)) {
puts("-1");
return 0;
}
ans+=min(cnt[1],cnt[2]);
}
printf("%d
",ans);
}
K. Bookcase Solidity United
有 (n) 块木板,第 (i) 块木板有稳定性 (a_i),木板从高到低排列。
在木板上放置铱球,第 (i) 块木板被放了大于等于 (a_i) 个铱球后会碎裂,有 (lfloorfrac k 2
floor) 会下坠到下一块木板上。
求至少需要多少铱球才可以砸碎前 (i) 块木板。
(1le nle 70),(1le a_ile 150)。
Solution
设 (f_{l,r,k}) 表示砸碎 ([l,r]) 的木板,留下 (k) 个球的最小球数。
有一个显然的转移:(f_{l,r,k}+max(a_{r+1}-k,0) o f_{l,r+1,max(k,a_{r+1})/2}),表示继承下若干个球来砸下面的。
同时,我们考虑分段砸,(f_{l,r,k_1+k_2}=f_{l,mid,k_1}+f_{mid+1,r,k_2})。
直接暴力转移是 (O(n^3m^2)) 的。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=200,M=200;
int n,a[N+10],m;
int f[N+10][N+10][M+10];
void chkmin(int &x,int y) {
if(x>y) x=y;
}
int main() {
scanf("%d",&n);
memset(f,0x3f,sizeof f);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),f[i][i][a[i]/2]=a[i],m=max(m,a[i]);
for(int len=2;len<=n;len++)
for(int l=1;l+len-1<=n;l++) {
int r=l+len-1;
for(int i=0;i<=m;i++) chkmin(f[l][r][max(i,a[r])/2],f[l][r-1][i]+max(0,a[r]-i));
for(int i=0;i<=m;i++)
for(int j=l;j<r;j++)
for(int k=0;k<=i;k++)
chkmin(f[l][r][i],f[l][j][k]+f[j+1][r][i-k]);
}
for(int i=1;i<=n;i++) {
int ans=INT_MAX;
for(int j=0;j<=m;j++) ans=min(ans,f[1][i][j]);
printf("%d ",ans);
}
puts("");
return 0;
}
M. Brilliant Sequence of Umbrellas
构造一个长度至少为 (lceil frac2 3 sqrt{n}
ceil) 的值域为 ([1,n]) 递增序列,使得两两之间的 (gcd) 也递增。
(1le nle 10^{12})。
Solution
观察样例,考虑构造一个每隔两个数均互质的序列,然后将这个序列相邻两项的积作为原序列。
这样一定满足 (gcd) 递增。
Code
typedef long long ll;
const int N=1e6;
ll n,a[N+10],cnt,b[N+10];
int main() {
scanf("%lld",&n);
b[++cnt]=1,b[++cnt]=1;
int lim=ceil(2.0*sqrt(n)/3);
for(ll i=2;cnt<=lim+1&&b[cnt]*i<=n;i++)
if(__gcd(b[cnt-1],i)==1)
b[++cnt]=i;
printf("%lld
",cnt-1);
for(int i=1;i<cnt;i++) printf("%lld ",b[i]*b[i+1]);
puts("");
}
N. Best Solution Unknown
有 (n) 个人打比赛,每个人有个实力值,实力值大的会打败实力值小的,相等的都有机会赢。
打赢一个人可以增加 (1) 的实力值,每个人只能和相邻的人打。
每次随机取比赛,求会有哪些人可能赢。
(1le nle 10^6),(1le a_ile 10^9)。
Solution
考虑对于一个大段,一个人能不能赢,抽取出最大值,发现最大值分成两份,一份是左一份是右,要想赢得翻过最大值这座大山。
那么我们就可以分治了,找出最大值来分治,每次记录左边或右边要至少多少才可以赢。
分治的时候用 ST 表加速求最值就可以了。
Code
const int N=1e6;
int a[N+10],n,Log[N+10],Max[N+10][30],id[N+10][30];
bool nb[N+10];
int query(int l,int r) {
int k=Log[r-l+1];
int ret=max(Max[l][k],Max[r-(1<<k)+1][k]);
if(ret==Max[l][k]) return id[l][k];
else return id[r-(1<<k)+1][k];
}
void dfs(int l,int r,int c) {
if(l>r) return;
if(l==r) {
if(a[l]>=c) nb[l]=1;
return;
}
int id=query(l,r);
if(a[id]<c) return;
nb[id]=1;
dfs(l,id-1,max(c,a[id]-id+1+l));
dfs(id+1,r,max(c,a[id]+id+1-r));
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
Log[1]=0;
for(int i=2;i<=n;i++) Log[i]=Log[i/2]+1;
for(int i=1;i<=n;i++) Max[i][0]=a[i],id[i][0]=i;
for(int j=1;j<=20;j++)
for(int i=1;i+(1<<(j-1))<=n;i++) {
Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);
if(Max[i][j]==Max[i][j-1]) id[i][j]=id[i][j-1];
else id[i][j]=id[i+(1<<(j-1))][j-1];
}
dfs(1,n,0);
int cnt=0;
for(int i=1;i<=n;i++) if(nb[i]) cnt++;
printf("%d
",cnt);
for(int i=1;i<=n;i++) if(nb[i]) printf("%d ",i);
puts("");
}