ZROI NOIP21 冲刺汇总
同步发布于hriver2.github.io
「启」
实际上之前是单独发的,但是因为每天一测,不一定能够补完当天的题目,所以就将所有的未补完的考试中已经补过的题放在这里。
四道题全部整完的会单独发出。
UPD 2021.11.8
「Day1」
讲课,略。
「Day2」
✅ ZR-NOIP21-20D-D2
已经整理完,博客为:总之就是 | ZROI NOIP21 冲刺 Day2。
「Day3」
⬜ ZR-NOIP21-20D-D3
「启」
今天在🐏的强码力支持下没有爆零(
缺省源和「这一篇」一致;
「A」
一道搞一搞出奇迹的题。
「A」题目简述
给出一个 \(3 \times 3\) 的矩阵,里面填入了 \(1\) 到 \(9\),求问最少进行几次操作可以使得这个矩阵满足:每行三个数之和为 \(15\),每列三个数之和为 \(15\),且每条对角线三个数之和也为 \(15.\)
操作即为交换相邻的两个数,题目为 \(T,(T \le 50)\) 组询问。
「A」思路简述
经过手玩/爆搜,发现如果我们把矩阵表述为一个长为 \(9\) 的序列,最终只有 \(8\) 种目标序列:
于是我们考虑去双向 BFS.
于是考虑一下这样做的复杂度:一共有 \(12\) 种操作,一个矩阵最多扩展出 \(9!\) 种状态,也就是说复杂度为 \(O(8 \times 9!T).\)
但是这样是过不去的,于是考虑把这个 \(8\) 的常数干掉,于是乆有了对于目标矩阵的离散化,所以最终的时间复杂度为 \(O(9!T).\)
在 BFS 的过程之中用到了 Hash 去判断重复状态(
「A」Code
template<typename J>
I J Hmin(const J &x,const J &y) {Heriko x<y?x:y;}
CI MOD(1000003),TXX(400000);
int n,Tco[10],f[10][10],sum,Sco[9][10],co[10],a[10];
int r[13][2];
void Pre()
{
for(int i(1);i<=9;++i) Tco[i]=i;
do
{
if (Tco[1]+Tco[2]+Tco[3] != 15 or Tco[4]+Tco[5]+Tco[6] != 15 or
Tco[7]+Tco[8]+Tco[9] != 15 or Tco[1]+Tco[4]+Tco[7] != 15 or
Tco[2]+Tco[5]+Tco[8] != 15 or Tco[3]+Tco[6]+Tco[9] != 15 or
Tco[1]+Tco[5]+Tco[9] != 15 or Tco[3]+Tco[5]+Tco[7] != 15)
continue;
++sum;
for (int i(1);i<=9;++i) f[sum][i]=Tco[i],Sco[sum][Tco[i]]=i;
puts("");
}
while(next_permutation(Tco+1,Tco+10));
r[1][0]=1,r[1][1]=2;
r[2][0]=2,r[2][1]=3;
r[3][0]=1,r[3][1]=4;
r[4][0]=2,r[4][1]=5;
r[5][0]=3,r[5][1]=6;
r[6][0]=4,r[6][1]=5;
r[7][0]=5,r[7][1]=6;
r[8][0]=4,r[8][1]=7;
r[9][0]=5,r[9][1]=8;
r[10][0]=6,r[10][1]=9;
r[11][0]=7,r[11][1]=8;
r[12][0]=8,r[12][1]=9;
}
struct Core
{
int id,ans;
};
struct Node
{
int nxt,key,cnt;
}
t[TXX];int head[MOD+10],tot;
I void Add(int x,int k) {t[++tot]=(Node){(int)head[x%MOD],x,k};head[x%MOD]=tot;}
I bool CheckBool(int x)
{
for (int i(head[x%MOD]);i;i=t[i].nxt)
if (t[i].key==x)
Heriko Romanno;
Heriko Deltana;
}
I int CheckInt(int x)
{
for (int i(head[x%MOD]);i;i=t[i].nxt)
if (t[i].key==x)
Heriko t[i].cnt;
Heriko Deltana;
}
int m[10];
I int Modify(int x,int id)
{
int temp(0),now,pw(1);
for(int i(9);i;--i) m[i]=(x%10),x/=10;
for (int i(9);i;--i)
{
now=m[i];
if (now==r[id][1]) now=r[id][0];
else if (now==r[id][0]) now=r[id][1];
temp+=pw*now;pw*=10;
}
Heriko temp;
}
I void BFS()
{
queue<Core> q;
q.push((Core){123456789,0});
while (!q.empty())
{
int x=q.front().id;
int k=q.front().ans;
q.pop();
if (CheckBool(x)) continue;
Add(x,k);
for (int i=1;i<=12;++i)
{
int Ner(Modify(x,i));
q.push((Core){Ner,k+1});
}
}
}
int main()
{
Files();
Pre();BFS();
int T;fr(T);
while (T--)
{
for(int i(1);i<=9;++i) fr(a[i]);
int ans(100);
for(int i(1);i<=8;++i)
{
for(int j(1);j<=9;++j) co[j]=Sco[i][a[j]];
int temp(0),pw(1);
for(int j(9);j;--j) temp+=pw*co[j],pw*=10;
ans = min(CheckInt(temp),ans);
}
fw(ans,1);
}
Heriko Deltana;
}
「Day4」
✅ ZR-NOIP-21-20D-D4
已经整理完,博客为:总之就是 | ZROI NOIP21 冲刺 Day4。
「Day5」
⬜ ZR-NOIP21-20D-D5
「启」
昨天上大分,今天挂大分,就当是一场梦,醒来还是很感动
缺省源使用「V5」.
「A」
这题我场上写假了两个之后,又交了个假的上去……
「A」题目简述
对于长度同为 \(n\) 的数 \(a\) 和 \(b\),定义其距离为 \(d(a,b) = \sum_{i=1}^n(a_i-b_i)^2.\)
现在你可以任意交换 \(a\) 的两个元素,求使得距离最短的最少交换次数。
求距离,若 \(opt=1\),输出最少交换次数。
数据范围:\(n \le 300000.\)
「A」思路简述
对于最短距离,sort 一下即可。
对于交换次数,我们用冰茶姬维护一下,再比较即可。
「A」Code
CI MXX(3e5+1),MOD(998244353);
struct Node
{
int val,id;
I bool operator < (const Node &co) const {Heriko val<co.val;}
}
a[MXX],b[MXX];
int n,opt,fa[MXX];
LL ans;
LL Find(int x)
{
if(fa[x]!=x) fa[x]=Find(fa[x]);
Heriko fa[x];
}
S main()
{
Files();
fr(n),fr(opt);
for(int i(1);i<=n;++i) fr(a[i].val),a[i].id=i;
for(int i(1);i<=n;++i) fr(b[i].val),b[i].id=i;
for(int i(1);i<=n;++i) fa[i]=i;
sort(a+1,a+1+n);sort(b+1,b+1+n);
for(int i(1);i<=n;++i) (ans+=1ll*(a[i].val-b[i].val)*(a[i].val-b[i].val)%MOD)%=MOD,fa[Find(a[i].id)]=Find(b[i].id);
fw((ans+MOD)%MOD,0);
if(opt)
{
ans=0;
for(int i(1);i<=n;++i) ans+=(fa[i]!=i);
fw(ans,1);
}
Heriko Deltana;
}
「B」
场上想的是正解,但是因为做 A 做的心态炸了所以场上就没写出来(
「B」题目简述
国际象棋中,主教这个棋子可以攻击所有与其在同一条斜线上的位置(自己所处位置也算在内),现在在 \(n \times n\) 的棋盘中给出 \(m\) 个主教,问棋盘中还有几个不会被攻击到的位置。
数据范围:\(1 \le n,m \le 10^6.\)
「B」思路简述
因为是 \(10^6\),即使是三秒时限,\(O(n^2)\) 也铁定过不去,所以考虑 \(O(n)\) 的做法。
考虑到一个棋子带来的影响只在两条直线上,所以我们考虑这两条直线的解析式:\(y_1=x+b_1,y_2=-x+b_2.\)
于是我们只需要记录 \(b_1\) 和 \(b_2\) 来判断边出现过没有即可。
因为所有的 \(y_1\) 都是平行的,所以我们只需要考虑其和所有 \(y_2\) 的交点即可,而这个可以前缀和优化。
于是乆做到了复杂度 \(O(n).\)
「B」Code
template<typename J>
I J Habs(const J &x) {Heriko x<0?-x:x;}
CI MXX(2e6+1);
int n,m,sum[MXX];
LL ans;
bitset<MXX> co[2];
S main()
{
Files();
fr(n),fr(m);
for(int i(1),x,y;i<=m;++i)
{
fr(x),fr(y);
int b1(x+y-1),b2(y-x+n);
co[0][b1]=co[1][b2]=1;
}
n<<=1;
for(int i(1);i<n;++i)
if(co[0][i])
{
int temp(Habs((n>>1)-i));
--sum[temp+1],++sum[n-temp+1];
}
for(int i(3);i<n;++i) sum[i]+=sum[i-2];
for(int i(1);i<n;++i)
if(i<=(n>>1)) sum[i]+=i;
else sum[i]+=n-i;
for(int i(1);i<n;++i)
if(co[1][i])
sum[i]=0;
for(int i(1);i<n;++i) ans+=sum[i];
fw(ans,1);
Heriko Deltana;
}
「Day6」
⬜ 21-NOIP21-20D-D6
「启」
今天这题给我做离谱了……希望几天之后的 CSP 不要这样出题……
各种毒瘤题,还卡常(
缺省源使用「V5」
「A」
这题场上因为读错题耽误了一个半小时,结果最后写完暴力发现又读错了题……我真是服了(
「A」题目复述
因为读错 \(114514\) 遍题,所以就不简述了吧(
「A」思路简述
因为第一页写在了第 \(t\) 页,根据题意,那么接下来的几天的页数会一天比一天小,然后会出现一个 \(p_i < p_{i+1}\)。然后去掉写过的这几页,又会重复这样的情况。
对于 \(q\) 中每一段连续的 \(q_i>q_{i+1}\),相当于就是要在剩下的页数里,选出若干页给这一段连续的 \(q_i>q_{i+1}\) 使用(由于第 \(1\) 页一定会选,所以计算组合数时上下都要减 \(1\)),于是就在排列 \(q\) 中找到所有 \(q_i<q_{i+1}\) 的 \(i\) 构成一个长度为 \(len\) 的数列 \(w\),那么答案为:
用 set
维护 \(w\),每次修改去修改前驱或后继即可。
「A」Code
我最一开始
ans
开了两个,一个全局,一个在main
里面,就因为这个调了两个小时。
CI MXX(3e5+1),MOD(1e9+7);
int n,m,a[MXX];
LL fac[MXX],inv[MXX],val[MXX],ans(1);
bitset<MXX> co;
set<LL> s;
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;
}
I void Pre()
{
fac[0]=1;
for(int i(1);i<=n;++i) fac[i]=fac[i-1]*i%MOD;
inv[n]=FstPow(fac[n],MOD-2);
for(int i(n-1);i>=0;--i) inv[i]=inv[i+1]*(i+1)%MOD;
}
I LL C(int x,int y) {Heriko ((fac[x]*inv[y]%MOD)*inv[x-y])%MOD;}
I void Add(int x)
{
auto i(s.lower_bound(x));
auto lst(i);
if(i==s.begin())
{
(ans*=C(n-1,x-1))%=MOD;
(ans*=FstPow(val[(*i)],MOD-2))%=MOD;
(ans*=C(n-x-1,(*i)-x-1))%=MOD;
val[x]=C(n-1,x-1);
val[(*i)]=C(n-x-1,(*i)-x-1);
}
else if(i==s.end())
{
--i;
(ans*=C(n-(*i)-1,x-(*i)-1))%=MOD;
val[x]=C(n-(*i)-1,x-(*i)-1);
}
else
{
--lst;
(ans*=C(n-(*lst)-1,x-(*lst)-1))%=MOD;
val[x]=C(n-(*lst)-1,x-(*lst)-1);
(ans*=FstPow(val[(*i)],MOD-2))%=MOD;
(ans*=C(n-x-1,(*i)-x-1))%=MOD;
val[(*i)]=C(n-x-1,(*i)-x-1);
}
s.insert(x);
}
I void Del(int x)
{
auto i(s.lower_bound(x));
auto lst(i);
auto org(i);
if(i==s.begin())
{
(ans*=FstPow(val[x],MOD-2))%=MOD;
++i;
(ans*=FstPow(val[(*i)],MOD-2))%=MOD;
val[(*i)]=C(n-1,(*i)-1);
(ans*=val[(*i)])%=MOD;
val[x]=0;
}
else
{
--lst,++i;
(ans*=FstPow(val[x],MOD-2))%=MOD;
if(i!=s.end())
{
(ans*=FstPow(val[(*i)],MOD-2))%=MOD;
(ans*=C(n-(*lst)-1,(*i)-(*lst)-1))%=MOD;
val[(*i)]=C(n-(*lst)-1,(*i)-(*lst)-1);
}
val[x]=0;
}
s.erase(org);
}
S main()
{
Files();
fr(n),fr(m);Pre();
for(int i(1);i<=n;++i) fr(a[i]);
int lsti(0);
for(int i(1);i<=n;++i)
{
co[i]=(a[i]<a[i+1]);
if(co[i])
{
(ans*=C(n-lsti-1,i-lsti-1))%=MOD;
val[i]=C(n-lsti-1,i-lsti-1);
s.insert(i);lsti=i;
}
}
fw(ans,1);
while(m--)
{
int x,y;fr(x),fr(y);
swap(a[x],a[y]);
int lstq;
if(x!=1)
{
lstq=co[x-1];
co[x-1]=(a[x-1]<a[x]);
if(lstq != co[x-1]) co[x-1]?Add(x-1):Del(x-1);
}
if(x!=n)
{
lstq=co[x];
co[x]=(a[x]<a[x+1]);
if(lstq != co[x]) co[x]?Add(x):Del(x);
}
if(y!=1)
{
lstq=co[y-1];
co[y-1]=(a[y-1]<a[y]);
if(lstq != co[y-1]) co[y-1]?Add(y-1):Del(y-1);
}
if(y!=n)
{
lstq=co[y];
co[y]=(a[y]<a[y+1]);
if(lstq != co[y]) co[y]?Add(y):Del(y);
}
fw(ans,1);
}
Heriko Deltana;
}
「Day7」
⬜ 21-NOIP21-20D-D7
「启」
4h 做 A,结果多测清空不规范,爆零两行泪。
可是就快要复赛了啊……还在爆零……
缺省源使用「V5」.
「A」
4h,两个假算法,多测清空不规范,TLE,爆零。
「A」题目简述
你参加了一个为期 \(D\) 天的夏令营,在此 \(1\) 到 \(D\) 天内有 \(N\) 个活动,其中第 \(i\) 个活动对你的吸引力是 \(h_i\) ,且将从第 \(s_i\) 天持续到第 \(e_i\) 天(包括 \(s_i\) 和 \(e_i\))。
每天你最多参加 kk 个活动,你想知道在此期间,哪一天的活动对你的吸引力总和最大。
数据范围:\(1 \le N,D \le 3\times 10^5.\)
「A」思路简述
枚举时间,将当前可选的活动加入时间序列,维护两个堆,一个小根堆表示现在选了哪些课程保持大小为 \(k\),另一个堆维护剩下的随时准备补位。
「A」Code
template<typename J>
I J Hmax(const J &x,const J &y) {Heriko x>y?x:y;}
CI MXX(3e5+2);
int d,n,k,h[MXX];LL ans;
bitset<MXX> in,no;
vector< pair<int,int> > v[MXX];
priority_queue< pair<int,int> > hp,thp;
I void Clear()
{
for(int i(1);i<=d+1;++i) v[i].clear();
while(hp.size()) hp.pop();
while(thp.size()) thp.pop();
in=no=0;
}
I void Solve()
{
int opt(0),x(0),sz(0),val(0),num(0);
LL sum(0);ans=0;
for(int i(1);i<=d;++i)
{
for(auto j:v[i])
{
opt=j.first,x=j.second;
if(opt)
{
if(sz<k)
{
sum+=h[x];++sz;in[x]=1;
hp.push(make_pair(-h[x],x));
}
else
{
while(no[hp.top().second]) hp.pop();
val=-hp.top().first;num=hp.top().second;
if(h[x]>val)
{
in[x]=1;in[num]=0;
sum+=(h[x]-val);
hp.pop();
hp.push(make_pair(-h[x],x));
thp.push(make_pair(val,num));
}
else thp.push(make_pair(h[x],x));
}
}
else
{
no[x]=1;
if(in[x])
{
sum-=h[x];--sz;
while (thp.size() && no[thp.top().second]) thp.pop();
if(thp.size())
{
val=thp.top().first,num=thp.top().second;thp.pop();
if(!no[num])
{
sum+=val,in[num]=1,++sz;
hp.push(make_pair(-val,num));
}
}
}
}
}
ans=Hmax(ans,sum);
}
}
S main()
{
Files();
int T;fr(T);
for(int ct(1);ct<=T;++ct)
{
Clear();fr(d),fr(n),fr(k);
for(int i(1),l,r;i<=n;++i)
{
fr(h[i]),fr(l),fr(r);
v[l].push_back(make_pair(1,i));
v[r+1].push_back(make_pair(0,i));
}
Solve();printf("Case #%d: %lld\n",ct,ans);
}
Heriko Deltana;
}
「Day8」
⬜ 21-NOIP21-20D-D8
「Day9」
⬜ 21-NOIP21-20D-D9
「启」
拉了。
缺省源使用「V5」.
「A」
场上想了一堆拉掉的玩意。
「A」题目简述
给定整数 \(N\),称它的一个分解为一组正整数 \(x_1,x_2,...,x_k\),满足以下条件:
-
\(\sum_{i = 1}^k x_i = N;\)
-
对于任意 \(1 \le i \le k\),都有 \(x_i=2^a3^b\),这里 \(a,b\) 均为非负整数;
-
对于任意 \(1 \le i,j \le k,i≠j\),\(x_i\) 都不是 \(x_j\) 的因子。
现在要求输出一种 \(N(N \le 10^{100})\) 的分解。
「A」思路简述
先将 \(N\) 不断除掉 \(2\),然后再不断减去最大的 \(3\) 的幂,答案最后要乘上那些 \(2.\)
这个数据范围需要高精(
「A」Code
CI MXX(105);
struct Pic
{
int num[MXX],sz;
Pic() {mst(num,0);sz=1;}
I void Clear() {mst(num,0);sz=1;}
I Pic operator * (const int &co) const
{
Pic res;res.sz=sz;
for(int i(1);i<=sz;++i) res.num[i]=num[i]*co;
for(int i(1);i<=sz;++i) res.num[i+1]+=(res.num[i]/10),res.num[i]%=10;
while(res.num[res.sz+1])
{
++res.sz;
res.num[res.sz+1]+=(res.num[res.sz]/10);
res.num[res.sz]%=10;
}
Heriko res;
}
I Pic operator * (const Pic &co) const
{
Pic res;res.sz=co.sz+sz;
for(int i(1);i<=sz;++i)
for(int j(1);j<=co.sz;++j)
{
res.num[i+j-1]+=(num[i]*co.num[j]);
res.num[i+j]+=(res.num[i+j-1]/10);res.num[i+j-1]%=10;
}
while(!res.num[res.sz] and res.sz>1) --res.sz;
Heriko res;
}
I Pic operator - (const Pic &co) const
{
Pic res;res.sz=sz;
for(int i(1);i<=sz;++i)
{
res.num[i]+=num[i]-co.num[i];
if(res.num[i]<0) --res.num[i+1],res.num[i]+=10;
}
while(!res.num[res.sz] and res.sz>1) --res.sz;
Heriko res;
}
I void RightShift()
{
for(int i(sz);i;--i)
{
if(num[i]&1) num[i-1]+=10;
num[i]>>=1;
}
while(!num[sz] and sz>1) --sz;
}
I bool operator > (const Pic &co) const
{
if(sz!=co.sz) Heriko sz>co.sz;
for(int i(sz);i;--i)
if(num[i]!=co.num[i])
Heriko num[i]>co.num[i];
Heriko Deltana;
}
I bool operator <= (const Pic &co) const {Heriko !((*this)>co);}
I void Into(char s[])
{
sz=strlen(s+1);
for(int i(1);i<=sz;++i) num[i]=s[sz-i+1]-'0';
}
I bool Zero() {Heriko (sz==1)&(num[1]==0);}
I void fw() {for(int i(sz);i;--i) putchar(num[i]+'0'); putchar(' ');}
}
n,ans[1005];
char s[105];
int anslen;
S main()
{
Files();
scanf("%s",s+1);n.Into(s);Pic pw;pw.num[1]=1;
while(!n.Zero())
{
while(!(n.num[1]&1)) pw=pw*2,n.RightShift();
Pic res,nex;res.num[1]=1;nex=res*3;
while(nex<=n) res=nex,nex=res*3;
n=n-res;res=res*pw;ans[++anslen]=res;
}
fw(anslen,1);
for(int i(1);i<=anslen;++i) ans[i].fw();
Heriko Deltana;
}
「Day10」
讲课,略。
「Day11」
⬜ 21-NOIP21-20D-D11
「启」
A 假了,B 场上以为假了但是其实没假,总结还是拉了。
缺省源使用「V5」.
「A」
Bad.
「A」题目简述
有一颗 \(n(n \le 10^6)\) 个节点的树,每个节点可能是红色或者黑色,每次操作可以选择一个节点,把这个节点所连的同色的结点反色,求把整棵树变成同一种颜色的最小操作次数。
「A」思路简述
因为相邻同色的结点可以看作一块,所有缩为一个点,然后对新树找直径,答案即为直径除 \(2.\)
「A」Code
template<typename J>
I J Hmax(const J &x,const J &y) {Heriko x>y?x:y;}
template<typename J>
I J Hmin(const J &x,const J &y) {Heriko x<y?x:y;}
CI MXX(1e6+1);
int n,f[MXX],ans;
struct Node
{
int nex,to;
}
r[MXX<<1];int cnt,head[MXX];
I void Add(int x,int y)
{
r[++cnt]=(Node){head[x],y};head[x]=cnt;
r[++cnt]=(Node){head[y],x};head[y]=cnt;
}
bitset<MXX> co;
void DFS(int x,int fa)
{
for(int i(head[x]);i;i=r[i].nex)
{
int y(r[i].to);
if(y==fa) continue;
DFS(y,x);
if(co[y]!=co[x])
{
ans=Hmax(ans,f[x]+f[y]+1);
f[x]=Hmax(f[x],f[y]+1);
}
else
{
ans=Hmax(ans,f[x]+f[y]);
f[x]=Hmax(f[x],f[y]);
}
}
}
I int GetCo()
{
char c(getchar());
while(c!='R' and c!='B') c=getchar();
Heriko c=='R'?1:0;
}
S main()
{
Files();
fr(n);
for(int i(1);i<=n;++i) co[i]=GetCo();
for(int i(1),x,y;i<n;++i) fr(x),fr(y),Add(x,y);
DFS(1,0);fw((ans+1)>>1,1);
Heriko Deltana;
}
「B」
场上最后的贪心策略是对的,但是当时以为假了。
「B」题目简述
给出一个长度为 \(k\) 的数列,然后给出 \(n\) 个操作,操作分为三种:
- \(a_i=b\)
- \(a_i=a_i+b\)
- \(a_i=a_i \times b\)
其中 \(i,b\) 是给定的,每个操作只能用一次,最多使用 \(m\) 个操作,让整个数列的乘积最大。
「B」思路简述
考虑将 Cover
操作转为 Add
操作,然后贪心的选取贡献权值大的即可。
「B」Code
template<typename J>
I J Hmax(const J &x,const J &y) {Heriko x>y?x:y;}
template<typename J>
I J Hmin(const J &x,const J &y) {Heriko x<y?x:y;}
CI MXX(1e5+1),MOD(1e9+7);
int n,k,m,cnt;
LL a[MXX],ans(1),cover[MXX];
struct CoFuc
{
int opt,pos;LL val;long double dlt;
}
co[MXX],ro[MXX];
I bool CMP(const CoFuc &x,const CoFuc &y) {Heriko x.val>y.val;}
I bool RMP(const CoFuc &x,const CoFuc &y) {Heriko x.dlt>y.dlt;}
S main()
{
Files();
fr(k),fr(n),fr(m);
for(int i(1);i<=k;++i) fr(a[i]);
for(int i(1);i<=n;++i)
{
fr(co[i].opt),fr(co[i].pos),fr(co[i].val);
if(co[i].opt==1) cover[co[i].pos]=Hmax(cover[co[i].pos],co[i].val);
}
for(int i(1);i<=k;++i)
if(cover[i]>a[i])
ro[++cnt]=(CoFuc){2,i,cover[i]-a[i],0.0};
for(int i(1);i<=n;++i)
if(co[i].opt!=1)
ro[++cnt]=co[i];
sort(ro+1,ro+1+cnt,CMP);
for(int i(1);i<=k;++i) cover[i]=a[i];
for(int i(1);i<=cnt;++i)
if(ro[i].opt==2)
{
ro[i].dlt=1.0*(cover[ro[i].pos]+ro[i].val)/(1.0*cover[ro[i].pos]);
cover[ro[i].pos]+=ro[i].val;
}
else if(ro[i].opt==3) ro[i].dlt=ro[i].val*1.0;
sort(ro+1,ro+1+cnt,RMP);
for(int i(1);i<=Hmin(m,cnt);++i)
if(ro[i].opt==2) a[ro[i].pos]+=ro[i].val;
else if(ro[i].opt==3) (ans*=ro[i].val)%=MOD;
for(int i(1);i<=k;++i) (ans*=a[i])%=MOD;
fw((ans+MOD)%MOD,1);
Heriko Deltana;
}
「C」
不是这怎么还出特判题呐?
「C」题目简述
小明有 \(1\) 到 \(n\) 共 \(n\) 包零食,同时他又有 \(1\) 到 \(n\) 共 \(n\) 个朋友。
昨天,小明的 \(n\) 个朋友都到他家来玩了。他的 \(n\) 个朋友瓜分了他的 \(n\) 包零食,每个人都恰好吃了一包零食,没有两个人吃了同一包零食。
小明发现,第 \(i\) 个朋友吃第 \(j\) 包零食能获得的愉悦值是 \(i\bmod j\)。
今天,小明想回忆起每个朋友吃的是哪包零食,他想不起来了,但是他却记得了所有人的愉悦值之和 \(s\)。于是,小明找上了你,请你构造出一种可能的方案。
由于小明记忆力不好,他有可能记错了 \(s\),所以可能会存在无解的情况。
「C」思路简述
特判题(
-
\(s\) 的上界为 \(\dfrac{n \times (n+1)}{2}\),超过乆输出无解。
-
\(s=0,1,2,\dfrac{n \times (n+1)}{2}-1\) 的时候详见代码,\(n\le 3\) 时同理。
-
剩余情况我们只需要构造出 \(2 \to n-1\) 的答案。
「C」Code
这里的
fw
函数不太一样,放一下。
template<typename J>
I void fw(bool g,J x,bool k)
{
if(!g)
{
puts("SPFA is dead!");
Heriko;
}
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(' ');
}
CI MXX(1e6+1);
LL n,s,res,ans[MXX],flg;
bitset<MXX> co;
S main()
{
Files();
fr(n),fr(s);
if(s>((n*(n-1))>>1)) Heriko fw(0,114514,1919810),Deltana;
if(n==1)
{
if(!s) puts("1");
else Heriko fw(0,114514,1919810),Deltana;
Heriko Deltana;
}
else if(n==2)
{
if(!s) puts("1\n2");
else if(s==1) puts("2\n1");
else Heriko fw(0,114514,1919810),Deltana;
Heriko Deltana;
}
else if(n==3)
{
if(!s) puts("1\n2\n3");
else if(s==1) puts("2\n1\n3");
else if(s==2) puts("3\n1\n2");
else if(s==3) puts("1\n3\n2");
else Heriko fw(0,114514,1919810),Deltana;
Heriko Deltana;
}
if(!s)
{
for(int i(1);i<=n;++i) fw(1,i,1);
Heriko Deltana;
}
else if(s==1)
{
puts("2\n1");
for(int i(3);i<=n;++i) fw(1,i,1);
Heriko Deltana;
}
else if(s==2)
{
puts("3\n1\n2");
for(int i(4);i<=n;++i) fw(1,i,1);
Heriko Deltana;
}
else if(s==((n*(n-1))>>1)-1)
{
if(n&1)
{
ans[1]=3,ans[2]=1,ans[n]=2;
for(int i(3);i<n;++i) ans[i]=i+1;
}
else
{
ans[1]=1;ans[n]=2;
for(int i(2);i<n;++i) ans[i]=i+1;
}
for(int i(1);i<=n;++i) fw(1,ans[i],1);
Heriko Deltana;
}
ans[n]=1;--s;
for(int i(n-1);i>=2;--i)
{
if(s>=i and s-i!=1) s-=i;
else ans[i]=i,co[i]=1;
}
int nw(n);
for(int i(n-1);i;--i)
if(!ans[i])
{
ans[i]=nw,co[nw]=1;
while(co[nw]) --nw;
}
for(int i(1);i<=n;++i) fw(1,ans[i],1);
Heriko Deltana;
}
「Day12」
✅ 21-NOIP21-20D-D12
已经整理完,博客为:总之就是 | ZROI NOIP21 冲刺 Day12。
「Day13」
⬜ 21-NOIP21-20D-D13
「启」
今天倒是没挂 A,但是大家都切 A,所以我挂 B 了/cy
缺省源使用「V5」.
「A」
切了好耶(
Time:1s Memory:512MiB.
「A」题目简述
要求找出满足下列条件最小的数:
-
这是一个正整数;
-
这个数至少有四个不同的因子;
-
这个数的任意两个因子之间的差不小于输入的 \(n(n \le 10^5)\)
「A」思路简述
因为要求两个任意两个因子之间的差不小于输入的 \(n\),所以想到选取质数。
而题目要求至少要有四个不同的因子,而一个数最小的因子是 \(1\),最大的因子是自己,所以我们只需要找到大于 \(n+1\) 的第一个质数和大于等于第一个选出来的质数 \(+n\) 后的最小的质数即可。
用一个欧拉筛解决质数问题即可。
「A」Code
CI MXX(1e7+5);
LL prime[MXX],cnt;
bool nopr[MXX];
I void Es(LL x)
{
nopr[1]=1;
for(LL i(2);i<=x;++i)
{
if(!nopr[i]) prime[++cnt]=i;
for(int j(1);j<=cnt and prime[j]*i<=x;++j)
{
nopr[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
LL n,T;
S main()
{
Files();
Es(MXX);
fr(T);
while(T--)
{
fr(n);LL fst(0),fstid(0),sed(0);
for(int i(1);i<=cnt;++i)
if(prime[i]>=n+1)
{
fst=prime[i];
fstid=i;
break;
}
for(int i(fstid);i<=cnt;++i)
if(prime[i]>=fst+n)
{
sed=prime[i];
break;
}
fw(sed*fst,1);
}
Heriko Deltana;
}
「B」
挂了好耶(
Time:1s Memory:512MiB.
「B」题目简述
给出一个有 \(n(n\le 1000)\) 个点的无向完全图,每次在图上去掉一个生成树中的所有边,问最多能进行多少次,并且输出每次要删掉哪些边。
「B」思路简述
首先能知道 \(n\) 个点的完全图的总边数为 \(\dfrac{n(n-1)}{2}\),而一个生成树的边数为 \(n-1\),所以能得出最多能进行 \(\left\lfloor\frac{n}{2}\right\rfloor\) 次删除操作。
那么下面考虑如何构造出每次删掉的生成树,场上是想的 DFS 删除,但是没判环于是挂掉了。
考完之后看题解发现可以反着来,考虑从 \(n-2\) 转移到 \(n\) 的时候,对于之前的每一个生成树 \(i\) 连上 \(2i-1\) 和 \(n-1\)、\(2i\) 和 \(n\),然后如果是偶数就再建一个新的,连边同理。
「B」Code
int n,m;
S main()
{
Files();
int T;fr(T);
for(int ct(1);ct<=T;++ct)
{
fr(n);m=(n>>1);
printf("Case #%d: %d\n",ct,m);
for(int i(1);i<=m;++i)
{
if(!(n&1)) fw((i+m)%n+1,0),fw(i+m,1);
for(int j(1);j<=(n-1)>>1;++j)
{
fw((i-j+n)%n+1,0),fw((i+j-1)%n+1,1);
fw((i+j-1)%n+1,0),fw((i-j+n-1)%n+1,1);
}
}
}
Heriko Deltana;
}
「C」
写不出 Check
好耶(
Time:3s Memory:512MiB.
「C」题目简述
现在要将一个 \(n(n\le10^5)\) 个结点的无向树分割成 \(k\) 个子树,使得这些子树中结点权值和最大的一颗最小。
「C」思路简述
这个题的答案求解……嗯是二分没错了,那么下面的问题就是如何去写 Check
(
然而场上没写出来 T_T
所以这个的 Check
怎么写呢(
考虑 DFS 去解决(
设 \(f(i)\) 表示当前结点 \(i\) 所在的联通块的点权和。对于每一个节点,将其所有孩子的 \(f\) 排序以后,从小到大依次往父亲里塞。
塞不下的,就只能切断了,也就是形成单独的联通块,塞进父亲里的,更新到父亲的 \(f\) 中,可以继续和上面的节点合并,然后就能求出最大值。
排序和统计用个 vector
即可。
「C」Code
template<typename J>
I J Hmax(const J &x,const J &y) {Heriko x>y?x:y;}
CI MXX(1e5+1);
struct Node
{
int nex,to;
}
r[MXX<<1];int cnt,head[MXX];
I void Add(int x,int y)
{
r[++cnt]=(Node){head[x],y};head[x]=cnt;
r[++cnt]=(Node){head[y],x};head[y]=cnt;
}
int n,k;
LL val[MXX],ans,f[MXX],sz[MXX];
vector<LL> sub[MXX];
void DFS(LL lmt,int x,int fa)
{
f[x]=sz[x]=0;sub[x].clear();
int res(0);sz[x]=val[x];
for(int i(head[x]);i;i=r[i].nex)
{
int y(r[i].to);
if(y==fa) continue;
DFS(lmt,y,x);++res;sub[x].push_back(sz[y]);f[x]+=f[y];
}
sort(sub[x].begin(),sub[x].end());
for(auto i:sub[x])
if(sz[x]+i<=lmt) sz[x]+=i,--res;
else break;
f[x]+=res;
}
I bool Check(LL x)
{
for(int i(1);i<=n;++i)
if(x<val[i])
Heriko Deltana;
DFS(x,1,0);
Heriko f[1]<k;
}
S main()
{
Files();
int T;fr(T);
for(int ct(1);ct<=T;++ct)
{
fr(n),fr(k);cnt=0;mst(head,0);
for(int i(1),x,y;i<n;++i) fr(x),fr(y),Add(x,y);
LL sm(0),mx(0);
for(int i(1);i<=n;++i) fr(val[i]),sm+=val[i],mx=Hmax(mx,val[i]);
if(n==k)
{
printf("Case #%d: %lld\n",ct,mx);
continue;
}
LL l(0),r(sm);
while(l<=r)
{
LL mid((l+r)>>1);
if(Check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
printf("Case #%d: %lld\n",ct,ans);
}
Heriko Deltana;
}
「Day14」
⬜ 21-NOIP21-20D-D14
「启」
哈哈又啥都没有,还是使用惊艳骗分。
缺省源使用「V5」.
「A」
不知道该说啥。
Time:2s,Memory:512MiB.
「A」题目简述
求一颗 \(n(n \le 2000)\) 的树上有多少点集 \(S\) 内的任意两点的最短距离为 \(d.\)
「A」思路简述
先处理出深度,然后按照深度从浅到深跑 DFS,每次选取之前已经选取过的点,然后计算子集大小 \(O(1)\) 回答询问。
「A」Code
CI MXX(2001),MOD(1e9+7);
struct Node
{
int nex,to;
}
r[MXX<<1];int cnt,head[MXX];
I void Add(int x,int y)
{
r[++cnt]=(Node){head[x],y};head[x]=cnt;
r[++cnt]=(Node){head[y],x};head[y]=cnt;
}
int n,dep[MXX],id[MXX];
LL pw2[MXX],tot[MXX],ans[MXX];
void DFS1(int x)
{
for(int i(head[x]);i;i=r[i].nex)
if(!dep[r[i].to])
dep[r[i].to]=dep[x]+1,DFS1(r[i].to);
}
bitset<MXX> vis;
void DFS2(int x,int fa,int stp)
{
if(vis[x]) ++tot[stp];
for(int i(head[x]);i;i=r[i].nex)
if(r[i].to!=fa)
DFS2(r[i].to,x,stp+1);
}
S main()
{
Files();
fr(n);
for(int i(1);i<=n;++i) id[i]=i;
pw2[0]=1;
for(int i(1);i<=n;++i) pw2[i]=(pw2[i-1]<<1)%MOD;
for(int i(1),x,y;i<n;++i) fr(x),fr(y),Add(x,y);
dep[1]=1;DFS1(1);sort(id+1,id+1+n,[](int x,int y){Heriko dep[x]<dep[y];});
for(int i(1);i<=n;++i)
{
vis[id[i]]=1;int tmp(1);
memset(tot,0,sizeof(LL)*(n+1));
DFS2(id[i],0,0);
for(int j(1);j<=n;++j)
{
(ans[j]+=(pw2[tot[j]]-1)*(pw2[tmp-1]))%=MOD;
tmp+=tot[j];
}
}
int T;fr(T);
while(T--)
{
int x;fr(x);fw(ans[x],1);
}
Heriko Deltana;
}
「Day15」
⬜ 21-NOIP21-20D-D15
「启」
哈哈又挂了 A,还是只有垃圾 \(10\) 分暴力。
缺省源使用「V5.1」.
「A」
挂了。
Time:1s,Memory:1GiB.
「A」题目简述
从 \(n(n \le 50)\) 个数中选出一个非空子集,求满足异或和等于与和,每个数都满足 \(\le 2^13.\)
「A」思路简述
我们设 \(f(i,j,k)\) 表示选取前 \(i\) 个数,与和为 \(j\),异或和为 \(k\) 的方案数,那么显然答案为 \(\sum\limits_{i=1}^{2^{n}}f(i,j,k)\times[j=k].\)
因为直接开会炸空间,所以我们滚掉第一维,状态就变成了 \(f(0/1,j,k)\),然后我们用一个栈来优化一下复杂度,要不然还是过不去(
「A」Code
CI NXX(51),MXX(1<<13),SXX(1<<21);
bitset<MXX> co[MXX];
int n,m,a[NXX],top[2];
LL f[2][MXX][MXX];
pair< int,int > stk[2][SXX];
LL ans;
S main()
{
Files();
fr(n);
for(int i(1);i<=n;++i) fr(a[i]),m|=a[i];
int nw(0);f[nw][m][0]=1;stk[nw][++top[nw]]=mkp(m,0);
for(int i(1),x,y;i<=n;++i)
{
nw^=1;int lst(nw^1);
for(int j(1);j<=top[nw];++j) x=stk[nw][j].first,y=stk[nw][j].second,f[nw][x][y]=0;
top[nw]=0;
for(int j(0);j<=m;++j) co[j]=0;
for(int j(1);j<=top[lst];++j)
{
x=stk[lst][j].first,y=stk[lst][j].second;
f[nw][x&a[i]][y^a[i]]+=f[lst][x][y];
if((x&a[i])==(y^a[i])) ans+=f[lst][x][y];
f[nw][x][y]+=f[lst][x][y];
if(!co[x][y]) co[x][y]=1,stk[nw][++top[nw]]=mkp(x,y);
x&=a[i],y^=a[i];
if(!co[x][y]) co[x][y]=1,stk[nw][++top[nw]]=mkp(x,y);
}
}
fw(ans,1);
Heriko Deltana;
}
「Day16」
⬜ 21-NOIP21-20D-D16
因为某些原因未参加。
「SC1」
⬜ 21-NOIP21-20D-SC1
同上。
「SC2」
⬜ 21-NOIP21-20D-SC2
「启」
因为某些事情,前几场考试没参加,这一场听说是信心场还 unr,就打一打玩。
缺省源使用「V5.2」.
「A」
诈骗题,但是场上写的结论少了点东西所以只有 \(80.\)
「A」题目简述
一个长度为 \(N\) 的字符串,字典集大小为 \(M\),要求连续长度为 \(K\) 的子串都必须是回文串,求方案数。
「A」思路简述
看起来没啥东西所以就觉得很诈骗(
分为以下几种情况:
-
\(k>n\) 或 \(k=1\) 时,答案为 \(m^n.\)
-
\(k=n\) 时,答案为 \(m^{\frac{n-1}{2}}.\)
-
否则,当 \(k\) 为 \(2\) 的倍数的时候,答案为 \(m^2\),若不是 \(2\) 的倍数,答案为 \(m.\)
「A」Code
CI MOD(1e9+7);
int n,m,k;
I int FstPow(int x,int y)
{
int res(1);
while(y)
{
if(y&1)
(res*=1ll*x)%=MOD;
(x*=1ll*x)%=MOD;
y>>=1;
}
Heriko res;
}
S main()
{
Files();
fr(n),fr(m),fr(k);
if(k>n or k==1)
fw(FstPow(m,n)%MOD,1);
else if(k==n)
fw(FstPow(m,(n+1)>>1),1);
else if(k&1)
fw((m*m)%MOD,1);
else if(!(k&1))
fw((m+MOD)%MOD,1);
Heriko Deltana;
}
「B」
考场写了个暴力,然后乆润了。
「B」题目简述
每次可以在矩阵中选择一行或一列加到答案中,然后把选择的这一列的每一项都减去 \(p.\)
「B」思路简述
考虑把行和列的操作拆开先预处理,然后最后合并答案的时候减去互相的影响。
「B」Code
template<typename J>
I J Hmax(const J &x,const J &y)
{
Heriko x>y?x:y;
}
CI MXX(1001),KXX(1e5+1);
CL INF(1145141919810);
int n,m,k,p,a[MXX][MXX];
LL ans(-INF),f[KXX],coi[KXX],coj[KXX],g[KXX];
priority_queue<LL> q;
S main()
{
Files();
fr(n),fr(m),fr(k),fr(p);
for(int i(1);i<=n;++i)
for(int j(1);j<=m;++j)
fr(a[i][j]);
for(int i(1);i<=n;++i)
for(int j(1);j<=m;++j)
coi[i]+=a[i][j],coj[j]+=a[i][j];
for(int i(1);i<=n;++i)
q.push(coi[i]);
for(int i(1);i<=k;++i)
{
LL x(q.top());
q.pop();
f[i]=f[i-1]+x,x-=p*m;
q.push(x);
}
while(q.size())
q.pop();
for(int i(1);i<=m;++i)
q.push(coj[i]);
for(int i(1);i<=k;++i)
{
LL x(q.top());
q.pop();
g[i]=g[i-1]+x,x-=p*n;
q.push(x);
}
for(int i(0);i<=k;++i)
ans=Hmax(ans,f[i]+g[k-i]-(1ll*i*(k-i)*p));
fw(ans,1);
Heriko Deltana;
}
「C」
是个大模拟,先不补。
「Day17」
⬜ 21-NOIP21-20D-D17
缺省源使用「V5.2」.
「启」
没啥好说的。
「A」
场上做出来了。
题目。
「A」思路简述
我们考虑对于每个点都用两个长度为 \(m\) 的二进制数来存储,分别记录第 \(i\) 位对应着第 \(i\) 次操作的时候的 \(X\) 或 \(Y\) 归属状态,那么最终两个点 \(i,j\) 之间是否有连边就等同于 \(X_i \operatorname{and} Y_j\) 的 \(1\) 的个数是否为奇数加上 \(X_j \operatorname{and} Y_i\) 的 \(1\) 的个数再减去两者交集。
「A」Code
CI NXX(2e4+1),MXX(64);
bitset<MXX> X[NXX],Y[NXX];
int n,m;
LL ans;
char s[NXX];
S main()
{
Files();
fr(n),fr(m);
for(int i(1);i<=m;++i)
{
scanf("%s",s+1);
for(int j(1);j<=n;++j)
{
Y[j][i]=(s[j]^48)&1;
X[j][i]=((s[j]^48)>>1)&1;
}
}
for(int i(1);i<n;++i)
for(int j(i+1);j<=n;++j)
ans+=((X[i]&Y[j]).count()+((Y[i]&X[j])^((X[i]&Y[j])&(Y[i]&X[j]))).count())&1;
fw(ans,1);
Heriko Deltana;
}
「B」
题目。
「B」思路简述
看到这个问题,就很自然地想到二分,于是设 \(b_i = a_i - x\),然后去二分这个 \(x.\)
然后把 \(b\) 的前缀和求出来,求长度为 \(k\) 的 LIS.
「B」Code
template<typename J>
I J Hmax(const J &x,const J &y)
{
Heriko x>y?x:y;
}
CI MXX(2e5+1);
const double INF(1e9);
int n,k,a[MXX],mx;
double ans,b[MXX],f[MXX];
I bool Check(double x)
{
for(int i(1);i<=n;++i)
b[i]=b[i-1]+a[i]-x;
int hd(0);
f[hd]=INF;
for(int i(1);i<=n;++i)
{
if(b[i]<0.0 or b[i]>b[n])
continue;
int pos(lower_bound(f,f+1+hd,b[i])-f);
f[pos]=b[i];
if(pos==hd)
f[++hd]=INF;
}
Heriko hd>=k;
}
S main()
{
Files();
fr(n),fr(k);
for(int i(1);i<=n;++i)
fr(a[i]),mx=Hmax(mx,a[i]);
double l(0),r(mx*1.0);
while(l+(1e-4)<r)
{
double mid((l+r)/2.0);
if(Check(mid))
l=mid,ans=mid;
else
r=mid;
}
printf("%.4lf",ans);
Heriko Deltana;
}
「C」
题目。
「C」思路简述
首先把每个无向边改为两个有向边,然后在每个结点上,可以任意匹配来自两条不同树边的边。
总的方案数为 \(2^{n-1}\),但是还有重复的方案,减去即可。
「C」Code
CI MXX(1e6+1),MOD(1e9+7);
LL f[MXX],n,inv[MXX],fac[MXX],r[MXX],sum[MXX],tot[MXX],ans(1);
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);
inv[0]=fac[0]=inv[1]=fac[1]=1;
for(int i(2);i<=n;++i)
{
inv[i]=(MOD-MOD/i)*1ll*inv[MOD%i]%MOD;
fac[i]=(fac[i-1]*1ll*i)%MOD;
}
for(int i(1);i<n;++i)
{
int x,y;
fr(x),fr(y);
++r[x],++r[y];
}
f[0]=tot[0]=sum[0]=1;
for(int i(1);i<=n;++i)
{
f[i]=(tot[i-1]+sum[i-1])%MOD;
f[i]=(f[i]-f[i-1]+MOD)%MOD;
f[i]=(1ll*f[i]*((MOD+1)>>1)%MOD*inv[i]%MOD);
sum[i]=(sum[i-1]+f[i])%MOD;
tot[i]=(tot[i-1]+sum[i])%MOD;
}
(ans*=FstPow(2,n-1))%=MOD;
for(int i=1;i<=n;i++)
ans=1ll*ans*fac[r[i]]%MOD*f[r[i]]%MOD;
fw((ans+MOD)%MOD,1);
Heriko Deltana;
}