2019.03.13 ZJOI2019模拟赛 解题报告
得分: \(55+12+10=77\)(\(T1\)误认为有可二分性,\(T2\)不小心把\(n\)开了\(char\),\(T3\)直接\(puts("0")\)水\(10\)分)
\(T1\):Bug 级的存在
感觉只有我这种智障才会觉得这题有可二分性。
显然,若要一段区间能够成为公差为\(d\)的等差序列,则一个基本要求就是这段区间要模\(d\)同余。
因此,我们首先自然就是把每段同余的区间单独抠出来处理。
然后我们把这段区间内的数同整除\(d\)。
但要注意,正数和负数同整除\(d\)可能会出问题,例如\(d=3\)时\(\lfloor\frac{-2}3\rfloor=\lfloor\frac13\rfloor=0\),而且负数在前面的取余操作中也略显麻烦。
因此,我们需要将每个数都减去这个序列中的最小值,这样就可以把每个数都变成非负整数了。
同整除\(d\)之后,对于一个符合条件的区间\([l,r]\),我们应满足其加上至多\(k\)个数后成为一个连续的序列。
即我们需要满足:\(max_{i=l}^ra_i-min_{i=l}^ra_i+1\le(r-l+1)+k\),且不存在相同的\(a_i\)。
之所以要满足这个不等式,因为不等式左半边表示若要使这段区间成为一个连续的序列至少需要的数的总数,右半边\(r-l+1\)表示这段区间内原有的数的个数,\(k\)表示至多可以加数的个数,显然需要数的总数应小于等于原有的数的个数与可以加数的个数。
然后考虑移项,可以化简得到:
\]
则不难发现,在确定\(r\),即确定右端点的情况下,我们显然就是要找到一个最小的\(l\),即左端点满足上述式子。
考虑到在我们从左往右枚举右端点的过程中,可以顺带开两个单调栈来维护\(min\)和\(max\),并考虑用线段树来维护每个点到当前\(r\)的\(max-min+l\)(\(l\)即为每个点的编号)。
最后直接在线段树上查询权值\(\le r+k\)的编号最小的点即可。
但要注意更新在线段树上查找时的下界,即前面提到过的附加条件不存在相同的\(a_i\)。
我们可以开一个\(map\)叫做\(lst\)来记录每个点上次出现的位置,记住每次将询问下界与\(lst[a_i]+1\)取\(min\),然后更新\(lst[a_i]=i\),就可保证不存在相同的\(a_i\)了。
最后,还有一个需要特判的地方,即\(d=0\),这样前面的取模与整除等运算会全部\(RE\)。
因此,我们要特判\(d=0\),而此时也很容易,只要\(O(n)\)扫一遍找出最长的一段元素全相同的区间即可。
代码如下:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define CL Con LL&
#define I inline
#define W while
#define N 200000
#define min(x,y) ((x)<(y)?(x):(y))
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Gmin(x,y) (x>(y)&&(x=(y)))
#define LL long long
#define INF 1e9
using namespace std;
int n,k,d,a[N+5],Mod[N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int f;char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0,f=1;W(!D) f=c^'-'?1:-1;W(x=tn+(c&15),D);x*=f;}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
#undef D
}F;
class DEqualsToZeroSolver//特判d=0的情况
{
public:
I void Solve()
{
RI i,t=1,ansl=0,ansr=0;
for(i=2;i<=n;++i) a[i]^a[i-1]&&((i-t>ansr-ansl+1)&&(ansl=t,ansr=i-1),t=i);//扫一遍求最长的一段元素全相同的区间
printf("%d %d",ansl,ansr);//输出答案
}
}Zero;
class SegmentTreeSolver//利用线段树维护信息求解答案
{
private:
int ansl,ansr,s[N+5],S1[N+5],S2[N+5];map<int,int> lst;
class SegmentTree//线段树
{
private:
#define STO l,hl,rt<<1
#define ORZ hl+1,r,rt<<1|1
#define PU(x) (O[x]=O[x<<1]+O[x<<1|1])
#define PD(x) (O[x].f&&(O[x<<1]+=O[x].f,O[x<<1|1]+=O[x].f,O[x].f=0))
int n;
struct Interval//存储区间信息
{
LL Mn,f;I Interval(CL m=0,CL p=0):Mn(m),f(p){}
I Interval operator + (Con Interval& t) Con {return Interval(min(Mn,t.Mn));}
I void operator += (CL x) {Mn+=x,f+=x;}
}O[N<<3];
I void upt(CI l,CI r,CI rt,CI ul,CI ur,CI v)//区间修改
{
if(ul<=l&&r<=ur) return O[rt]+=v;PD(rt);RI hl=l+r>>1;
ul<=hl&&(upt(STO,ul,ur,v),0),ur>hl&&(upt(ORZ,ul,ur,v),0),PU(rt);
}
I int ask(CI l,CI r,CI rt,CI v)//查询权值小于等于v的最左端点
{
if(O[rt].Mn>v) return INF;if(PD(rt),!(l^r)) return l;
RI hl=l+r>>1;return O[rt<<1].Mn<=v?ask(STO,v):ask(ORZ,v);
}
I int qry(CI l,CI r,CI rt,CI ql,CI qr,CI v)//查询给定区间内权值小于等于v的最左端点(先找到区间,然后调用ask函数)
{
if(PD(rt),ql<=l&&r<=qr) return ask(l,r,rt,v);RI hl=l+r>>1,res=INF,t;
return ql<=hl&&(t=qry(STO,ql,qr,v),Gmin(res,t)),qr>hl&&(t=qry(ORZ,ql,qr,v),Gmin(res,t)),res;
}
I void Build(CI l,CI r,CI rt)//建树,即清空线段树
{
if(!(l^r)) return (void)(O[rt]=Interval(),0);RI hl=l+r>>1;
Build(STO),Build(ORZ),PU(rt);
}
public:
I void Init(CI x) {n=x,Build(1,n,1);}I void Update(CI l,CI r,CI v) {upt(1,n,1,l,r,v);}
I int Query(CI l,CI r,CI v) {RI t=qry(1,n,1,l,r,v);return min(t,r);}
}T;
public:
I SegmentTreeSolver() {ansl=ansr=1;}
I void Solve(CI l,CI r)
{
RI i,t,cnt=0,T1=0,T2=0,L=1;for(i=l;i<=r;++i) s[++cnt]=a[i]/d;//把区间内所有数整除d的结果存储下来
for(T.Init(cnt),lst.clear(),i=1;i<=cnt;++i)//清空线段树和map,然后枚举右端点i
{
W(T1&&s[S1[T1]]<=s[i]) T.Update(S1[T1-1]+1,S1[T1],s[i]-s[S1[T1]]),--T1;//单调栈维护最小值,同时在线段树上修改-min
W(T2&&s[S2[T2]]>=s[i]) T.Update(S2[T2-1]+1,S2[T2],s[S2[T2]]-s[i]),--T2;//单调栈维护最小值,同时在线段树上修改max
Gmax(L,lst[s[i]]+1),lst[s[i]]=S1[++T1]=S2[++T2]=i,T.Update(i,i,i),//更新询问下界,将i加入栈,并在线段树中i这一位上增加i(因为i之后会被作为左端点,因此要将它加上自身编号)
t=T.Query(L,i,i+k),i-t>ansr-ansl&&(ansl=l+t-1,ansr=l+i-1);//询问最小的满足要求左端点,然后更新答案
}
}
I void PrintAns() {printf("%d %d",ansl,ansr);}//输出答案
}S;
int main()
{
freopen("bug.in","r",stdin),freopen("bug.out","w",stdout);
RI i,t=INF;for(F.read(n,k,d),i=1;i<=n;++i) F.read(a[i]);if(!d) return Zero.Solve(),0;//读入数据,特判d=0的情况
for(i=1;i<=n;++i) Gmin(t,a[i]);for(i=1;i<=n;++i) Mod[i]=(a[i]-=t)%d;Mod[n+1]=-1;//将所有数化为非负整数,然后存下模d的余数
for(t=1,i=2;i<=n+1;++i) Mod[i]^Mod[i-1]&&(S.Solve(t,i-1),t=i);return S.PrintAns(),0;//抠出区间分别处理,然后输出答案
}
\(T2\):猪队友
考试时只会写乱搞。
我们可以设\(f_{i,j}\)表示当\(s_i=j\)时使区间\([i,n]\)内\(s\)与\(t\)相同的最小步数,\(g_{i,j}\)表示先使\(s_i=j\),然后使区间\([i+1,n]\)内\(s\)与\(t\)相同的最小步数。
然后考虑转移\(f_{i,j}\):
如果枚举的\(j\)与\(t_i\)相同,则我们无需进行操作即可得到\(s_i=t_i\),直接从上一位转移过来,即:\(f_{i,j}=f_{i+1,s_{i+1}}\)。
否则,第一种情况,我们直接替换这一位,即:\(f_{i,j}=f_{i+1,s_{i+1}}+1\)。
第二种情况,如果\(s\)的上一位与\(t\)的这一位相同,则我们可以交换这两位,即:\(f_{i,j}=f_{i+1,j}+1\)。
然后还有第三种比较复杂的情况,即我们先把上一位变成\(t_i\),且保证区间\([i+2,n]\)内\(s\)与\(t\)相同(不难发现,这就相当于\(g_{i+1,t_i}\)),然后我们把上一位与这一位交换(步数加\(1\)),然后比较\(j\)与\(t_{i+1}\)是否恰好相同,如果不同则需要替换\(j\)为\(t_{i+1}\)(步数再加\(1\)),即:\(f_{i,j}=g_{i+1,b_i}+1+[j==b_{i+1}]\)。
\(g_{i,j}\)的转移与其类似。
最后的答案就是\(f_{1,s_1}\),即在\(s_1=s_1\)时,使整个\(s\)与\(t\)相等的步数。
代码如下:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define C 26
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,a[N+5],b[N+5],f[N+5][C+5],g[N+5][C+5];char s[N+5],t[N+5];
int main()
{
freopen("teammate.in","r",stdin),freopen("teammate.out","w",stdout);
RI i,j;for(scanf("%s%s",s+1,t+1),n=strlen(s+1),i=1;i<=n;++i) a[i]=s[i]&31,b[i]=t[i]&31;//a与b分别把s与t的字符转化为数字存下来
for(i=1;i<=C;++i) f[n][i]=b[n]^i?1:0,g[n][i]=a[n]^i?1:0;for(i=n-1;i;--i) for(j=1;j<=C;++j)//初始化状态,然后枚举状态进行转移
{
j^b[i]?(f[i][j]=f[i+1][a[i+1]]+1,!(a[i+1]^b[i])&&Gmin(f[i][j],f[i+1][j]+1),Gmin(f[i][j],g[i+1][b[i]]+(j^b[i+1]?1:0)+1)):f[i][j]=f[i+1][a[i+1]];
j^a[i]?(g[i][j]=g[i+1][b[i+1]]+1,!(a[i+1]^j)&&Gmin(g[i][j],f[i+1][a[i]]+1),Gmin(g[i][j],g[i+1][j]+(a[i]^b[i+1]?1:0)+1)):g[i][j]=g[i+1][b[i+1]];
}printf("%d",f[1][a[1]]);//输出答案
}
\(T3\):不可能完成的任务
我们首先需要证明两个结论:
结论\(1\):若\(F(a)=t,F(b)=t+C\),则\(a+b=2t-1\)。
证明如下:
由\(F(a)=t,F(b)=t+C\)得:
\[F(a)+C=F(b)
\]可以发现这与题目中给出的\(F(x)+C=F(2F(x)-x+1)\)类似,对应可得:
\[a=x,b=2F(x)-x+1,t=F(a)=F(x)
\]然后代入式子\(a+b\)中计算可得:
\[a+b=x+(2F(x)-x+1)=2F(x)+1=2t+1
\]结论\(2\):\(F(a+2C)=F(a)+2C\)
证明如下:
我们依然像上面一样,设\(F(a')=t',F(b')=t'+C\),依然可得:
\[F(a')+C=F(b')
\]两边同时加上一个\(C\),可以得到:
\[F(a')+2C=F(b')+C
\]设\(F(c')=F(b')+C\),则\(F(c')=F(a')+2C\)。然后把\(F(c')=F(b')+C\)代入前面的结论\(1\),就可以得到其对应关系为:
\[a=b',b=c',t=F(a)=F(b')=F(a')+C
\]因此,我们就可以得到:
\[b'+c'=2(F(a')+C)+1=2F(a')+2C+1=2t'+2C+1=2C+(2t'+1)
\]同样由前面的结论\(1\),我们可知\(2t'+1=a'+b'\),代入得:
\[b'+c'=2C+a'+b'
\]两边同时减去\(b'\)可得:
\[c'=a'+2C
\]代入\(F(c')=F(a')+2C\)可得:
\[F(a'+2C)=F(a')+2C
\]
那么证明这两个结论有什么用呢?
由结论\(2\),我们可知,只要知道\(F\)这个函数在\(0\sim2C-1\)范围内的值,就可以由此推得整个函数的值了。
而由结论\(1\),\(a+b=2t+1\),我们可知,\(a\)与\(b\)的奇偶性显然不同。
若假设\(a\)为偶数,则我们可以设\(a=2x,b=2y+1\),且\(0\le a,b\le2C-1\)。
然后设\(F(a)=t\),则我们需要满足下面这个式子才能使\(a\)和\(b\)成功配对:
\]
其中\(n\)为某一未知数。
然后把这个式子移项,可以得到:
\]
把\(a=2x,b=2y+1\)代入,可以得到:
\]
两边同减\(1\),然后就能发现系数都为\(2\),然后同除以\(2\)即可得到:
\]
而\(F(b+2nC)=F(a)+C\),所以:
\]
总结可以得到:
\]
接下来我们考虑题目中给出的\(X_i,Y_i\)。
假设\(X_i=a(mod\ 2C)\),则可设:
\]
因此:
\]
\]
假设:
\]
所以我们就可以得到:
\]
同理,假设\(X_i=b(mod\ 2C)\),则可设:
\]
因此:
\]
\]
\]
假设:
\]
所以我们就可以得到:
\]
于是,最后结果就是\(\sum |W_i+nC|\),显然,根据初一数学可知,\(-nC\)应该尽量接近\(W\)的中位数。
这样,我们就可以求出任意的两个\(x,y\)造成的最小的代价\(p_{x,y}\)。
注意若\(W_i=nC+W'(0\le W'\le C-1)\),则最终使答案最小的应为\(nC\)或者\((n+1)C\)中的一种,需要分别讨论。
接下来,我们要将\(x\)与\(y\)一一配对,就需要做带权二分图匹配。
这里我们考虑写一个状压\(DP\),可以设\(f_i\)表示\(y\)已经被匹配的子集为\(i\)时的最小代价。
若设\(g_i\)表示\(i\)在二进制下\(1\)的个数,由于\(x\)是按顺序匹配的,则显然此时\(x\)被匹配的是前\(g_i\)个,即第\(0\sim g_i-1\)个。
所以就可以推出转移方程为:
\]
具体实现详见代码。
代码如下:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 10000
#define C 16
#define LL long long
#define Gmin(x,y) (x>(y)&&(x=(y)))
#define INF 1e18
using namespace std;
int n,c,s[N+5],X[N+5],Y[N+5],g[1<<C];LL p[C+5][C+5],f[1<<C];
int main()
{
freopen("mission.in","r",stdin),freopen("mission.out","w",stdout);
RI i,j,k,t,px,ax,mx,py,ay,my,S,lim;LL s1,s2;
scanf("%d%d%d%d%d%d%d%d%d%d",&c,&n,&X[1],&px,&ax,&mx,&Y[1],&py,&ay,&my);//读入
for(i=2;i<=n;++i) X[i]=(1LL*X[i-1]*px+ax)%mx,Y[i]=(1LL*Y[i-1]*py+ay)%my;//生成数据
for(i=0;i^c;++i) for(j=0;j^c;++j)//枚举x,y
{
for(t=0,k=1;k<=n;++k) X[k]%(c<<1)==(i<<1)?s[++t]=X[k]-Y[k]-i+j:(X[k]%(c<<1)==(j<<1|1)&&(s[++t]=-X[k]+Y[k]-i+j-c+1));//存储W[i]
if(!t) continue;sort(s+1,s+t+1),S=(s[t+1>>1]%c+c)%c-s[t+1>>1],s1=0,s2=0;//求出中位数
for(k=1;k<=t;++k) s1+=abs(s[k]+S);for(S-=c,k=1;k<=t;++k) s2+=abs(s[k]+S);p[i][j]=min(s1,s2);//分别讨论nC和(n+1)C,记录任意的两个x,y造成的最小的代价p[x][y]
}
for(i=1,lim=1<<c;i^lim;++i) for(g[i]=g[i>>1]+(i&1),f[i]=INF,j=0;j^c;++j) (i>>j)&1&&Gmin(f[i],f[i^(1<<j)]+p[g[i]-1][j]);//状压DP做带权二分图匹配
return printf("%lld",f[lim-1]),0;//输出答案
}
2019.03.13 ZJOI2019模拟赛 解题报告的更多相关文章
- 2019.03.19 ZJOI2019模拟赛 解题报告
得分: \(100+10+45=155\)(\(T1\)又是水题,\(T2\)写暴力,\(T3\)大力\(STL\)乱搞) \(T1\):哈夫曼树 首先,根据题目中给出的式子,可以发现,我们要求的其实 ...
- 2019.03.02 ZJOI2019模拟赛 解题报告
得分: \(10+0+40=50\)(\(T1\),\(T3\)只能写大暴力,\(T2\)压根不会) \(T1\):道路建造 应该是一道比较经典的容斥题,可惜比赛时没有看出来. 由于要求最后删一条边或 ...
- 2019.03.09 ZJOI2019模拟赛 解题报告
得分: \(20+0+40=60\)(\(T1\)大暴力,\(T2\)分类讨论写挂,\(T3\)分类讨论\(40\)分) \(T1\):天空碎片 一道神仙数学题,貌似需要两次使用中国剩余定理. 反正不 ...
- 2019.03.14 ZJOI2019模拟赛 解题报告
得分: \(100+100+0=200\)(\(T1\)在最后\(2\)分钟写了出来,\(T2\)在最后\(10\)分钟写了出来,反而\(T3\)写了\(4\)个小时爆\(0\)) \(T1\):风王 ...
- 2019.03.15 ZJOI2019模拟赛 解题报告
得分: \(20+45+15=80\)(三题暴力全写挂...) \(T1\):Lyk Love painting 首先,不难想到二分答案然后\(DP\)验证. 设当前需验证的答案为\(x\),则一个暴 ...
- 2019.03.16 ZJOI2019模拟赛 解题报告
得分: \(100+27+20=147\)(\(T1\)巨水,\(T2,T3\)只能写暴力分) \(T1\):深邃 比较套路的一眼题,显然是一个二分+贪心,感觉就是\(NOIP2018Day1T3\) ...
- 10.30 NFLS-NOIP模拟赛 解题报告
总结:今天去了NOIP模拟赛,其实是几道USACO的经典的题目,第一题和最后一题都有思路,第二题是我一开始写了个spfa,写了一半中途发现应该是矩阵乘法,然后没做完,然后就没有然后了!第二题的暴力都没 ...
- 20201101gryz模拟赛解题报告
写在前面 2020rp++ 停课的第一场模拟赛 拿上一年的上一年的day1来考的, 结果得分期望220pts,实际135pts,rank3,太菜了 考着考着机房灯突然灭了,当时慌的一批 以为断电代码要 ...
- 2018.10.26NOIP模拟赛解题报告
心路历程 预计得分:\(100 + 100 + 70\) 实际得分:\(40 + 100 + 70\) 妈妈我又挂分了qwq..T1过了大样例就没管,直到临考试结束前\(10min\)才发现大样例是假 ...
随机推荐
- Python的Profilers性能分析器
关于Python Profilers性能分析器 关于性能分析,python有专门的文档,可查看:http://docs.python.org/library/profile.html?highligh ...
- JAVA生成word的几种方法对比
首先介绍几种java导出word方案 1.Jacob是Java-COM Bridge的缩写,它在Java与微软的COM组件之间构建一座桥梁.使用Jacob自带的DLL动态链接库,并通过JNI的方式实现 ...
- 安装Chrome插件Markdown Preview Plus
1.在谷歌应用商店,安装Chrome插件Markdown Preview Plus 2.设置Markdown Preview Plus (1)鼠标左键该拓展插件 (2)鼠标右键该插件 3.将mar ...
- pandas DataFrame数据转为list
dfpath=df[df['mm'].str.contains('20180122\d')].values dfplist=np.array(dfpath).tolist()
- OneDrive撸5T硬盘空间教程
注意:要注册多个账户获取网盘的,用无痕模式打开临时教育邮箱网址.打开之后不要关闭,等会用来接收验证码. 1.需要office 365注册这时候需要教育邮箱: 临时教育邮箱:http://sysu.ed ...
- SolrCloud的搭建的连接
1 什么是SolrCloud SolrCloud(solr 云)是Solr提供的分布式搜索方案,当你需要大规模,容错,分布式索引和检索能力时使用SolrCloud.当一个系统的索引数据量少的时候是不需 ...
- 深入学习hbase:表,列族,列标识,版本和cell
HBase是面向列的分布式的数据库,和传统的关系型数据库有很大的不同:物理模型和逻辑模型.这里我们要首先讲一下HBase数据库相关的区别于关系型数据库的几个基本概念: 表:HBase ...
- Cordova 常用的插件汇总
1.分享到微信:cordova-plugin-wechat 地址:https://github.com/xu-li/cordova-plugin-wechat 安装: cordova plugin a ...
- intellijidea课程 intellijidea神器使用技巧2-2 精准搜索
高效定位: 1 类: 类的跳转: Ctrl shift n ==> 查询类名 Ctrl shift n n ==> jar包中的类 2 文件: Ctrl shift shift n ==& ...
- 各种推导式<"一行能解决的事,为什么要用那么多行">
一.推导式 1.列表:[结果 for循环 条件筛选] 2.字典:{k:v for循环 条件筛选} 3.集合推导式{k for循环 条件筛选} ???为什么没有元组推导式 二.生成器表达式(元组表达式) ...