(联考)noip87
T1
我是sb,考场推半天用当前A集合中的和来算贡献的式子,结果死活不对。
正解很简单....
每一个值对答案的贡献是一样的 \(\sum_{i=2}^{n\times m+1}inv_{i}\times(i-1)\) 所以直接算即可,最后再乘上\(inv_{n\times m}\) 。
T2
30pts:只能生成一个数,选最大的即可。
100pts:
期望要倒着推我是sb,设 \(f_{i}\) 表示后面 \(i\) 个的期望,已经算好后 \(i\) 个,那么只需跟当前的判断一下即可,大于就替换,否则继续枚举。
卡精度,要开long double 。
也是sb题
T3
考场一眼笛卡尔树,但不会维护区间乘积,于是死了....
40pts:直接暴力。
40+20pts:\(l=r\) 直接推一下式子就行。
80pts:用线段树维护区间乘积,\(O(n\log{n})\)。
100pts:
建一颗大根堆笛卡尔树,dfs一遍,从下往上合并信息。
设每个点管辖的区间为 \([l,r]\) ,对于每个点都维护一个 \(pre_{i},suf_{i},pi_{i}\) 分别表示 \(\sum_{i=l}^{r}\prod_{j=l}^{i}a_{j},\sum_{i=r}^{l}\prod_{j=r}^{i}a_{j},\prod_{i=l}^{r}a_{i}\) 。
设点 \(ls_{i},rs_{i}\) 分别为 \(i\) 的左儿子和右儿子。
那么由 \(ls_{i},rs_{i}\) 向上合并到点 \(i\) 时,由于点 \(i\) 管辖的区间 \([l,r]\) 是由 \([l_{ls_{i}},r_{ls_{i}}]+[i,i]+[l_{rs_{i}},r_{rs_{i}}]\) 得来,所以有:
自己画个图就不难理解
特殊的,如果点 \(i\) 为叶子节点,则 \(pi_{i}=pre_{i}=suf_{i}=a_{i}\) 。
那这题就没了...
Code
#include<cstdio>
#include<cctype>
#include<vector>
#define re register
const int MAX = 1e7+3;
#define long long long
#define scanf oma=scanf
#define freopen file=freopen
int oma;
FILE* file;
namespace some
{
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
bool w=0; s=0; char ch=getchar();
while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
return s=w?-s:s,*this;
}
}cin;
int ans;
int n,s,l,r,mod;
int a[MAX],cnt;
#define debug(s) printf("%s\n",s)
auto max = [](int a,int b) { return a>b?a:b; };
}using namespace some;
namespace GenHelper
{
unsigned z1, z2, z3, z4, b;
auto rand_ = []() -> unsigned
{
b = ((z1 << 6) ^ z1) >> 13;
z1 = ((z1 & 4294967294U) << 18) ^ b;
b = ((z2 << 2) ^ z2) >> 27;
z2 = ((z2 & 4294967288U) << 2) ^ b;
b = ((z3 << 13) ^ z3) >> 21;
z3 = ((z3 & 4294967280U) << 7) ^ b;
b = ((z4 << 3) ^ z4) >> 12;
z4 = ((z4 & 4294967168U) << 13) ^ b;
return (z1 ^ z2 ^ z3 ^ z4);
};
} // namespace GenHelper
void get (int n, unsigned s, int l, int r) {
using namespace GenHelper;
z1 = s;
z2 = unsigned((~s) ^ 0x233333333U);
z3 = unsigned(s ^ 0x1234598766U);
z4 = (~s) + 51;
for (int i = 1; i <= n; i ++) {
int x = rand_() & 32767;
int y = rand_() & 32767;
a[++cnt] = l+(x*32768+y)%(r-l+1);
}
}
namespace simple
{
int mul,xam;
auto solve = []() -> void
{
for(re int i=1; i<=n; i++)
{
mul = 1,xam = 0;
for(re int j=i; j<=n; j++)
{ mul = 1ll*mul*a[j]%mod,xam = max(xam,a[j]),(ans += 1ll*mul*xam%mod) %= mod; }
}
printf("%d\n",ans);
};
};
namespace subtask
{
auto task = [](int mi = 1) -> void
{
for(re int i=1; i<=n; i++)
{ mi = 1ll*mi*l%mod,(ans += 1ll*(n-i+1)*mi%mod*l%mod) %= mod; }
printf("%d\n",ans);
};
};
namespace right
{
int rt;
long ans;
struct Cartesian_Tree
{
int ch[MAX][2];
int sta[MAX],top;
long pre[MAX],suf[MAX],pi[MAX];
#define ls(p) ch[p][0]
#define rs(p) ch[p][1]
void build()
{
pi[0] = 1;
for(re int i=1; i<=n; i++)
{
while(top&&a[sta[top]]<a[i])
{ ls(i) = sta[top--]; }
if(top)
{ rs(sta[top]) = i; }
sta[++top] = i;
}
rt = sta[1];
}
void dfs(int p)
{
if(ls(p))
{ dfs(ls(p)); }
if(rs(p))
{ dfs(rs(p)); }
pi[p] = pi[ls(p)]*pi[rs(p)]%mod*a[p]%mod;
if(!ls(p)&&!rs(p))
{ pre[p] = suf[p] = a[p]; }
else
{
pre[p] = (pre[ls(p)]+pi[ls(p)]*a[p]%mod+pi[ls(p)]*a[p]%mod*pre[rs(p)]%mod)%mod,
suf[p] = (suf[rs(p)]+pi[rs(p)]*a[p]%mod+pi[rs(p)]*a[p]%mod*suf[ls(p)]%mod)%mod;
}
(ans += (suf[ls(p)]+1)*a[p]%mod*(pre[rs(p)]+1)%mod*a[p]%mod) %= mod;
}
void check()
{
for(re int i=1; i<=n; i++)
//{ printf("%d %d\n",ch[i][0],ch[i][1]); }
{ printf("pre=%lld suf=%lld %d\n",pre[i],suf[i],a[i]); }
}
}Tree;
auto solve = []() -> void
{
Tree.build(),Tree.dfs(rt);
//Tree.check();
printf("%lld\n",ans);
};
};
namespace OMA
{
auto main = []() -> signed
{
freopen("tio.in","r",stdin); freopen("tio.out","w",stdout);
cin >> n >> s >> l >> r >> mod; get(n,s,l,r);
//if(l==r) { subtask::task(); }
//else { simple::solve(); }
right::solve();
return 0;
};
};
signed main()
{ return OMA::main(); }
T4
有个式子 \(\mu(n)^{2}=\sum_{d^{2}|n}\mu(d)\) 。
那这题就没了...
前边筛出来后求个前缀和,后边可以用整除分块解决。
取模直接用unsigned long long 自然溢出,但还是会乘爆,后边式子求和时注意/2的顺序即可。