总之就是 | ZROI NOIP21 冲刺 Day2
「启」
在连续三场爆零之后,连续上分三场,本以为脱离了爆零魔咒,但是这次还是爆零了(
是 Day2 的原因是 Day1 是杂题选讲,所以就没有 Day1 了(
「缺省源」
#include <bits/stdc++.h>
#define Heriko return
#define Deltana 0
#define Romanno 1
#define S signed
#define LL long long
#define R register
#define I inline
#define CI const int
#define mst(a, b) memset(a, b, sizeof(a))
#define ON std::ios::sync_with_stdio(false);cin.tie(0)
#define Files() freopen("RNMTQ.in","r",stdin);freopen("RNMTQ.out","w",stdout)
using namespace std;
template<typename J>
I void fr(J &x)
{
short f(1);x=0;char c=getchar();
while(c<'0' or c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while (c>='0' and c<='9')
{
x=(x<<3)+(x<<1)+(c^=48);
c=getchar();
}
x*=f;
}
template<typename J>
I void fw(J x,bool k)
{
if(x<0) x=-x,putchar('-');
static short stak[35];short top(0);
do
{
stak[top++]=x%10;
x/=10;
}
while(x);
while(top) putchar(stak[--top]+'0');
k?puts(""):putchar(' ');
}
「A」数字变换
这题虽然场上的结论是对的,但是推出来的柿子太【数据销毁】我【数据销毁】不会算。
「A」题目简述
现对于二元组 \((x,y)\),可以有以下变换:
-
变为 \((2x\bmod p,(y+p-x)\bmod p);\)
-
变为 \(((x+p-y)\bmod p,2y\bmod p).\)
给出两个二元组 \((a,b)\) 和 \((c,d)\),求问能否经过变换后使 \((a,b)\) 变为 \((c,d)\)。
若能,输出需要变换几次,否则输出 \(-1.\)
「A」思路简述
设 \(s=a+b\),发现在经过变换之后,在 \(\bmod p\) 意义下 \(s\) 的值是不变的,所以我们可以利用这一点来进行判断是否有解,即为判断 \(a+b\) 是否等于 \(c+d.\)
因为 \(s\) 是一定的,所以我们可以只考虑 \(a\)。发现 \(a\) 在经过变换之后只会变为 \(2^ka-ts,(t \in [0,2^k))\),于是就有 \(c=2^ka-ts.\)
我们考虑在经历了 \(k\) 次操作之后,剪掉一个 \(c\) 看看有多少个 \(s\),如果 \(s\) 的数量大于 \(2^k\),说明存在一个 \(t\) 使得上式成立,所以最后的柿子是:
枚举 \(k\) 即可。
「A」Code
LL MOD,T;
I LL FstPow(LL x,LL y)
{
LL res(1);
while(y)
{
if(y&1) (res*=x)%=MOD;
(x*=x)%=MOD;y>>=1;
}
Heriko res;
}
S main()
{
Files();
fr(MOD),fr(T);
while(T--)
{
LL a,b,c,d;
fr(a),fr(b),fr(c),fr(d);
if(((a+b)%MOD)!=((c+d)%MOD)) {puts("-1");continue;}
if(a==c and b==d) {puts("0");continue;}
LL k(1),Invs(FstPow(a+b,MOD-2)%MOD);
while(((1<<k)*a%MOD-c+MOD)*Invs%MOD>=(1<<k)) ++k;
fw(k,1);
}
Heriko Deltana;
}
「B」均分财产
场上并没有想出来什么(
但是貌似很好乱搞的样子,正解的部分证明也是建立在数据随机上的(
「B」题目简述
给出有 \(n\) 个数的序列 \(a_i\)(\(a_i \le 2 \times 10^5\) 且随机),任意删除不超过 \(k,(\min(25,n-2) \le k \le n-2)\) 个数,要求将剩下的数分为两个和相等的可重集合。
「B」思路简述
由于 \(k\) 范围的特殊性,所以进行分类讨论(bushi
-
当 \(k \le 25\) 的时候,暴力枚举所有的子集来找元素和相等的两个集合,设为 \(S_1,S_2\),最终答案即为 \(S_1'=S_1-S_1 \cap S_2,S_2'=S_2-S_1 \cap S_2\),复杂度为 \(O(n^2).\)
-
当 \(k>25\) 时,先对前 \(n-25\) 个元素进行贪心的分类,若当前的差值 \(dlt<0\),\(a_i\) 就分到 \(S_1\),\(dlt=dlt+a_i\),否则分到 \(S_2\),\(dlt=dlt-a_i\)。而剩下的 \(25\) 个元素只需要暴力枚举所有子集即可。时间复杂度为 \(O(n+2^{25}).\)
对于这 \(25\) 个元素的所有子集,一定有元素和小于等于 \(25W\),而一共有 \(2^{25}\) 个子集,比 \(26W\) 大许多,由于是随机数据,几乎一定会出现两个集合元素差为 \(dlt\) 的情况,在这个题这里就是相当于一定有解。
最终用的 \(O(3^n)\) 暴力枚举子集。
「B」Code
template<typename J>
I J Hmin(const J &x,const J &y) {Heriko x<y?x:y;}
CI MXX(1e6+1);
int n,k,m,a[MXX],dlt,co[MXX][3],s[MXX],ans(-1);;
void DFS(int t,int cnt,int lsm,int rsm)
{
if(~ans) Heriko;
if(t==m+1)
{
if(lsm-rsm==dlt) ans=1;
Heriko;
}
if(cnt<k and !(~ans)) s[t]=0,DFS(t+1,cnt+1,lsm,rsm);
if(!(~ans)) s[t]=1,DFS(t+1,cnt,lsm+a[t],rsm);
if(!(~ans)) s[t]=2,DFS(t+1,cnt,lsm,rsm+a[t]);
}
S main()
{
Files();
fr(n),fr(k);
m=Hmin(n,25);int tot[3]={0,0,0};
for(int i(1);i<=n;++i) fr(a[i]);
for(int i(26);i<=n;++i)
if(dlt>0) dlt-=a[i],co[++co[0][1]][1]=i;
else dlt+=a[i],co[++co[0][2]][2]=i;
DFS(1,0,0,0);
if(!(~ans)) {puts("-1");Heriko Deltana;}
for(int i(1);i<=m;++i) ++tot[s[i]];
fw(tot[1]+co[0][1],0);
for(int i(1);i<=co[0][1];++i) fw(co[i][1],0);
for(int i(1);i<=m;++i)
if(s[i]==1)
fw(i,0);
putchar('\n');
fw(tot[2]+co[0][2],0);
for(int i(1);i<=co[0][2];++i) fw(co[i][2],0);
for(int i(1);i<=m;++i)
if(s[i]==2)
fw(i,0);
Heriko Deltana;
}
「C」查询工资
据说是场上最难拿全分的一道题?但是我都爆零了我还管这?(大嘘
「C」题目简述
给定一棵共有 \(n\) 个节点且以 \(1\) 为根节点的树,同时给定参数 \(k.\)
每个节点上有一个权值,现在有两种询问权值的操作(不能直接询问):
-
若 \(x\) 的子节点个数 \(\ge k\),则可以得到其所有儿子的权值之和。
-
若以 \(x\) 为根的子树中包含的点 \(>k\),则可以得到这个子树的权值和(包括 \(x\))。
如果一个点是叶子节点,则可以被删除,求经过适当的删除操作之后可以通过询问得到权值的点的个数。
「C」思路简述
先不看如何去删除,先来考虑如何能得到一个结点的权值:
-
如果 \(x\) 的子树大小 \(>k\),那么它的权值可以通过其父亲为根的子树减去以 \(x\) 为根的子树的权值和得到;
-
如果 \(x\) 不为叶子且的子树大小 \(\le k\),那么它和它的子树中的点的权值是无法区分的,不能得到 \(x\) 的权值;
-
如果 \(x\) 为叶子节点,它的权值只能是通过减去其父亲子树中其它结点来得到,那么就需要它满足:
-
\(x\) 没有兄弟;
-
\(x\) 的父亲的父亲(祖父)至少要有 \(k\) 个儿子;
-
\(x\) 的祖父的其它子树要不然是叶子节点,要不然子树大小 \(>k.\)
-
考虑用 \(f(x)\) 去记录以 \(x\) 为根的子树(不包含自己)中能得到权值的点的最大数量。
那么分别对上面的情况进行考虑:
-
若 \(x\) 的儿子 \(y\) 出现第一种情况,尝试用 \(f(y)+1\) 更新 \(f(x);\)
-
若有一个子树大小 \(\le k\) 的儿子,且 \(x\) 有 \(\ge k\) 个儿子,可以将它删成大小为 \(2\) 的子树。那么可以将所有子树大小不足 k+1 的儿子删为叶子,那么此时可以用所有儿子的 \(f_sum+1\) 进行更新。
「C」Code
template<typename J>
I J Hmax(const J &x,const J &y) {Heriko x>y?x:y;}
CI MXX(8e5+1);
int n,k;
struct Node
{
int nex,to;
}
r[MXX];int cnt,head[MXX];
I void Add(CI &x,CI &y) {r[++cnt]=(Node){head[x],y};head[x]=cnt;}
int sz[MXX],son[MXX],f[MXX];
void DFS(int x)
{
sz[x]=1;son[x]=f[x]=0;bool flg(0);
for(int i(head[x]);i;i=r[i].nex)
{
int y(r[i].to);
DFS(y);sz[x]+=sz[y];f[x]+=f[y];++son[x];
if(!f[y] and sz[y]>1) flg=1;
}
if(son[x]>=k and flg) ++f[x];
for(int i(head[x]);i;i=r[i].nex)
if(sz[r[i].to]>k)
f[x]=Hmax(f[x],f[r[i].to]+1);
}
S main()
{
Files();
int T,x;fr(T);
while(T--)
{
cnt=0;mst(head,0);
fr(n),fr(k);
for(int i(2);i<=n;++i) fr(x),Add(x,i);
DFS(1);fw(f[1],1);
}
Heriko Deltana;
}
「D」多项式题
就普遍理性而论,这种标着多项式的题的都不用多项式。
「D」题目简述
有一个长度为 \(n\) 的数字串,定义其一种划分的权值为划分形成的若干个十进制数字的乘积,求其所有划分的权值和,\(n \le 2 \times 10^5.\)
「D」思路简述
场上最一开始读错题了(
设 \(f(i)\) 为将前 \(i\) 位划分为若干段的价值之和,答案即为 \(f(n).\)
根据乘法分配律可以得到:
其中 \(val(x,y)\) 代表第 \(x\) 到第 \(y\) 位置表示的十进制数字。
直接计算这个柿子的复杂度为 \(O(n^2)\),过不去,于是考虑进行优化。
我们从 \(val\) 考虑优化,对于上式,我们有:
那么重新定义 \(val(i)\) 为前 \(i\) 位组成的十进制数,原柿就变成了:
我们再用一步优化,即设 \(g(i)=g(i-1)+f(i),h(i)=h(i-1)+10^{-i}\times val(i) \times f(i).\)
这样就能在 \(O(n)\) 的时间复杂度下求解 \(f(i).\)
「D」Code
CI MXX(2e5+1),MOD(998244353);
int n;
char s[MXX];
LL f[MXX],inv[MXX],val[MXX],pw10[MXX],g[MXX],h[MXX];
I LL FstPow(LL x,LL y)
{
LL res(1);
while(y)
{
if(y&1) (res*=x)%=MOD;
(x*=x)%=MOD;y>>=1;
}
Heriko res;
}
S main()
{
Files();
fr(n);scanf("%s",s+1);pw10[0]=f[0]=g[0]=1;
for(int i(1);i<=n;++i) pw10[i]=pw10[i-1]*10%MOD;
for(int i(0);i<=n;++i) inv[i]=FstPow(pw10[i],MOD-2);
for(int i(1);i<=n;++i) val[i]=(val[i-1]*10+s[i]-48+MOD)%MOD;
for(int i(1);i<=n;++i)
{
f[i]=((val[i]*g[i-1]%MOD)-(pw10[i]*h[i-1]%MOD)+MOD)%MOD;
g[i]=(g[i-1]+f[i])%MOD;
h[i]=(h[i-1]+(val[i]*f[i]%MOD*inv[i]%MOD)+MOD)%MOD;
}
fw(f[n],1);
Heriko Deltana;
}
「终」
其实还有好多没写完(
所以时间上的顺序好像也不太对的样子。
全是正睿,氪金多了属于是(
距离 CSP-S2 就 10 天了,RP++.