一次询问 n n n个串,第 i i i个串第 i i i个位置为0,其余均为1
答案就是 m i n ( n ! , m o d ) min(n!,mod) min(n!,mod)
不过出题人似乎并没有考虑 1 1 1的时候答案是 2 2 2(询问 1 1 1和 0 0 0都可以)
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
long long mod,x,i,res[1000005];
int main()
{
mod = 1000003;
res[0] = 1;
fo(i,1,mod) res[i] = (res[i-1] * i) % mod;
while (~scanf("%d",&x))
{
if (x == 1) cout<<2<<endl; else
if (x >= mod) cout<<0<<endl; else
{
cout<<res[x]<<endl;
}
}
return 0;
}
区间查询前 44 44 44大的数,每次找最大两个数判断
std给出了 n l o g 2 n ∗ 44 nlog_2n*44 nlog2n∗44的做法
我用线段树暴力维护区间最大值,T了。。。。。
最后是主席树一次性整体查询 K K K大值
#include
#define N 100000
using namespace std;
int a[N],b[N],root[N*20],ls[N*20],rs[N*20],sum[N*20];
int res[N];
int n,q,i,tot,k,ql,qr,qk;
void build(int l,int r,int &rt)
{
rt = ++tot;
sum[rt] = 0; if (l == r) return;
int m = (l + r) >> 1;
build(l,m,ls[rt]);
build(m+1,r,rs[rt]);
}
void update(int l,int r,int &rt,int last,int p)
{
rt = ++tot;
ls[rt] = ls[last]; rs[rt] = rs[last];
sum[rt] = sum[last] + 1;
if (l == r) return;
int m = (l + r) >> 1;
if (p <= m) update(l,m,ls[rt],ls[last],p);
else update(m+1,r,rs[rt],rs[last],p);
}
void query(int ss,int tt,int l,int r,int kl,int kr)
{
if (l == r) {for (int i = kl;i <= kr; i++) res[i] = -b[l]; return;}
int m = (l + r) >> 1;
int cnt = sum[ls[tt]] - sum[ls[ss]];
if (kr - kl + 1 <= cnt)
query(ls[ss],ls[tt],l,m,kl,kr);
else
{
query(ls[ss],ls[tt],l,m,kl,kl+cnt-1);
query(rs[ss],rs[tt],m+1,r,kl+cnt,kr);
}
}
int main()
{
while (~scanf("%d%d",&n,&q))
{
for (i = 1;i <= n; i++) scanf("%d",&a[i]);
for (i = 1;i <= n; i++) a[i] = -a[i];
for (i = 1;i <= n; i++) b[i] = a[i];
sort(b+1,b+n+1);
k = unique(b+1,b+n+1) - (b+1);
for (i = 1;i <= n; i++) a[i] = lower_bound(b+1,b+k+1,a[i])-b;
tot = 0;
build(1,k,root[0]);
for (i = 1;i <= n; i++) update(1,k,root[i],root[i-1],a[i]);
while (q--)
{
scanf("%d%d",&ql,&qr);
qk = min(qr-ql+1,45);
query(root[ql-1],root[qr],1,k,1,qk);
long long ans = -1;
for(i = 3;i <= qk; i++)
{
if (res[i-2] - res[i-1] < res[i])
{
ans = 1ll*res[i-2] + res[i-1] + res[i];
printf("%I64d\n",ans);
break;
}
}
if (ans == -1) printf("-1\n");
}
}
return 0;
}
场上想了一个巨麻烦的做法
首先合法情况下每个数至少出现 K K K次,那么如果有某个数出现小于 K K K次,那么将这个序列分成 K + 1 K+1 K+1段进行递归
不过这样做没有优化就会被卡掉。。。
比如: 55454343232121 55454343232121 55454343232121
每次只会去掉最后三个数,然后就会退化成 N 2 N^2 N2
可以用启发式合并,也就是说每次只做较短的区间,然后用总区间减去小区间得到大区间
不过 s t d std std的做法更巧妙
固定右端点,对于每种元素,他的合法区间一定是两段连续区间: