昨天 又考模拟赛 考场上心态再次爆炸 导致一道题爆零。
总结 :慌了..当T1看了30min还不知道怎么做的时候我就已经慌了..觉得要GG了。(以后还是不能慌 把送的分数都拿到手才是正道...
考试策略还行 大概是先写自己会写的暴力 再写陌生的题目。放题么?还是放吧 反正没人看..(当然..
当然 考试是最暴露出自己的短板的 可能这次不爆下次就爆了。
![](https://img2018.cnblogs.com/blog/1485523/201909/1485523-20190916111141731-1946427781.png)
具体的在讨论这道题的解法之前 应该先复习一下CRT和EXCRT...
CRT:如何求解?由于m都是互质的 所以说可以考虑构造解这里我设m=lcm($b_1$,$b_2$,$b_3$...$b_n$); 那么对于每一个同余方程 设Mi=m/$b_i$;我们可以得到再求一个东西 $M_iT_i$$equiv$ 1(mod$b_i$)
关于整个方程的解:x=$sum_{i=1}^{n}M_iT_ia_i$ 那么这玩意就是整个方程的解了 我们观察一下是否合法 对于某一个方程其中有一项模的时候不为0剩下的全部都为0 然后对于$M_iT_ia_i$这个东西。
我们先人发现 等式两边是相同的东西 所以也成立 由此我们得到了CRT的解法 求解 $T_i$即可。
LINK:[猜数字CRT模板](https://www.luogu.org/problem/P3868) 这道题是模板题但是这里仍要写一遍exgcd的代码害怕遗忘.
``` if(b==0) { x=1,y=0; return; } exgcd(b,a%b,x,y); ll z=x;x=y;y=z-a/b*y; ```
还算是比较好推的。继续这道题 我们发现对于都是互质的情况一定有解 通过构造的方法显然可以证明。那么对于这道题的20points都是质数 但是可能有相同的 只要相同的质数我们给他们取的ai相同即可,所以相当于都是互质的所以无论取什么ai答案都是0
考虑非互质的情况:还是我们先来一道EXCRT 再讨论什么时候会无解。
EXCRT 解模数非互质的同余方程组 怎么做?对于第一个方程我们显然能够直接求 如何搞出来全部的 还是构造 对于我们已经求出了前k-1个解 那么此时我们记m=lcm($m_1,m_2,m_3...m_k-1$);
此时显然 对于前k个方程的通解为x+$t*m$ 那么对于第k个方程 那么我们其实要求的是x+tm$equiv$ $a_k$(mod $m_k$); 是否有解还是利用exgcd的那一套 即判断gcd的方法...
LINK:[屠龙勇士(我想到了昂热](https://www.luogu.org/problem/P4774).
我还没有 屠龙的决心 我A不了这道题 不知道哪里出现了错误 觉得自己写的没有什么大问题。以后再写一遍吧。(有一个地方有疑惑 回来问学长好了
经过我的不懈钻研 终于明白了哪个地方写挂了 哪个地方想错了。不过这些错误我都记得不真切所以以后还得复习。 ```
//“不要与昂热为敌”,那种男人心里藏着煤矿,怒火被点燃就再不熄灭,直到烧死敌人,或者烧死自己。
const int MAXN=100010;
ll T,n,m,flag;
ll sword[MAXN];
ll maxx,M,x,y,a,b,c,xx,G;
ll s[MAXN],p[MAXN];
multiset
multiset
inline ll exgcd(ll aa,ll bb)
{
if(!bb){x=1,y=0;return aa;}
ll w=exgcd(bb,aa%bb);
ll z=x;x=y;y=z-aa/bby;
return w;
}
inline ll mul(ll b,ll p,ll mod)
{
ll cnt=0;
b=b%mod;p=p%mod;
while(p)
{
if(p&1)cnt+=b;
b=b2%mod;
p=p>>1;
}
return cnt;
}
int main()
{
//freopen("1.in","r",stdin);
T=read();
while(T--)
{
n=read();m=read();
q.clear();flag=0;maxx=0;xx=0;M=1;
for(int i=1;i<=n;++i)s[i]=read();
for(int i=1;i<=n;++i)p[i]=read();
for(int i=1;i<=n;++i)sword[i]=read();
for(int i=1;i<=m;++i)q.insert(read());
for(int i=1;i<=n;++i)
{
if(p[i]!=1)flag=1;
it=q.upper_bound(s[i]);
if(it!=q.begin())--it;
int w=it;
q.erase(it);q.insert(sword[i]);
sword[i]=w;
}
if(!flag)
{
for(int i=1;i<=n;++i)
{
ll w=s[i]/sword[i];
if(s[i]%sword[i])++w;
maxx=max(maxx,w);
}
printf("%lld
",maxx);
continue;
}
flag=0;
for(int i=1;i<=n;++i)
{
b=p[i];a=mul(sword[i],M,b);
c=((s[i]-mul(xx,sword[i],b))%b+b)%b;
G=exgcd(a,b);
if(c%G){flag=1;break;}
b=b/G;
x=mul(x,c/G,b);
xx=(xx+xM);
M=M*b;
xx=xx%M;
}
if(flag)puts("-1");
else printf("%lld
",(xx%M+M)%M);
}
return 0;
}
<p>但是我却已经对EXCRT理解颇深了 没白练...
<p>那么现在可以考虑本题了。经过屠龙的历练我们显然的知道对于质因数都就是0。
<p>深入的去思考 ai>=0且ai<m 那么我们对于每个方程暴力枚举一个ai统计答案即可这里尽量使用dfs的形式边枚举边计算
<p>EXCRT 这样复杂度大概是nmlogai的 65分就到手了...
<p>如果我考试的时候对这个东西非常的熟悉该有多好啊。还是自己太懒的没有学的非常精通。以后不会再这样了。
<p>考虑100分的做法 我们的m到达了1e18的地步,这样我们只能从 EXCRT的本质下手观察其x的分布。
<p>设 M=lcm($m_1,m_2,m_3...m_n$);那么 对于一个x 其通解是 x+k*m也就是说只有M个x带入我们的到的{a}才会本质不同。
<p>这个时候考虑{a}的取值如果取这M个{a}之外的必然使我们得到的答案不合法所以我们总答案是 $prod_{i=1}^{n}m_i$ - M 这样就是整道题的答案了关键分析出来本质不同的ai。从答案的角度去分析。
<p>code:
n=read();
for(ll i=1;i<=n;++i)
{
ll x=read();
ll G=gcd(x,M);
M=Mx/G;
m=mx;
}
printf("%lld
",m-M);
<p>![](https://img2018.cnblogs.com/blog/1485523/201909/1485523-20190917162448483-1781989347.png)
<p>这道题就比较有意思了 题解之中说的有点繁杂 这里我来简化一个比较容易写的东西。
<p>看完题目显然是dp 对于偶数显然不需要再考虑 对于奇数我们必然要去掉一个点 显然去掉两个点还是不合法的 那么去掉三个点显然没有没有去掉一个点更优,归纳一下发现只需要去掉一个点。
<p>去掉这个点 整张图变成了偶数的情况那么 这张残图上的点就有了答案,考虑对于去掉这个点的那个联通块来说他们的答案是去掉另一个个点。
<p>我们先观察大局 发现去掉一个点使得所有最优即可 选取去掉这个点的代价最小的即可。显然这个点的集合是所有叶子节点向上爬到的第一个关灯的节点 那么我们将其贡献统计出来取出最小的即可。
<p>另外的,对于去掉的那部分其选取次小的作为贡献即可。上述已证明了该做法的正确性。
//#include<bits/stdc++.h>
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
define INF 1000000000
define ll long long
define R register
define db double
define max(x,y) ((x)>(y)?(x):(y))
define v(p) t[p].v
using namespace std;
char buf[1<<15],fs,ft;
inline char getc()
{
return (fsft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fsft))?0:fs++;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x10+ch-'0';ch=getchar();}
return x*f;
}
const int MAXN=1000010;
int n,len,tot,id,cnt,h,t,minn=INF,s,minx=INF;
int a[MAXN],pos[MAXN],sz[MAXN],q[MAXN];
int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1];
int lc[MAXN],vc[MAXN<<1],nc[MAXN<<1];
int ru[MAXN],vis[MAXN],w[MAXN],mark[MAXN];
inline void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
inline void add_c(int x,int y)
{
vc[++tot]=y;
nc[tot]=lc[x];
lc[x]=tot;
}
inline void bfs()
{
memset(vis,0,sizeof(vis));
h=t=0;
q[++t]=1;
while(h++<t)
{
int x=q[h];vis[x]=1;
if(!pos[x])pos[x]=++id;
if(a[x])++sz[pos[x]];
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(vis[tn])continue;
if(a[x]&&a[tn])pos[tn]=pos[x];
else pos[tn]=++id;
q[++t]=tn;
if(pos[tn]!=pos[x])
{
add_c(pos[x],pos[tn]);add_c(pos[tn],pos[x]);
++ru[pos[x]];++ru[pos[tn]];
}
}
}
}
inline void topsort()
{
memset(vis,0,sizeof(vis));
t=h=0;
for(int i=1;i<=id;++i)if(ru[i]1)q[++t]=i;
while(h++<t)
{
int x=q[h];vis[x]=1;w[x]+=sz[x];
if(!sz[x])
{
if(w[x]<minn)
{
minx=minn;
minn=w[x];
s=x;
}
else if(w[x]<minx)minx=w[x];
continue;
}
for(int i=lc[x];i;i=nc[i])
{
int tn=vc[i];
if(vis[tn])continue;
w[tn]+=w[x];
--ru[tn];
if(ru[tn]1)q[++t]=tn;
}
}
for(int i=lc[s];i;i=nc[i])
{
int tn=vc[i];
if(!vis[tn])continue;
mark[tn]=1;
}
mark[s]=1;
}
int main()
{
//freopen("1.in","r",stdin);
freopen("hide.in","r",stdin);
freopen("hide.out","w",stdout);
n=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
if(!a[i])++cnt;
}
if(!(cnt&1))
{
for(int i=1;i<=n;++i)
printf("%d
",n);
return 0;
}
for(int i=1;i<n;++i)
{
int x,y;
x=read();y=read();
add(x,y);add(y,x);
}
bfs();
topsort();
for(int i=1;i<=n;++i)
{
if(mark[pos[i]])
{
if(minx==INF)printf("%d
",sz[pos[i]]);
else printf("%d
",n-minx-1);
}
else printf("%d
",n-minn-1);
}
return 0;
}
<p>T3:![](https://img2018.cnblogs.com/blog/1485523/201909/1485523-20190917163511932-30132080.png)
<p>看起来很难的样子 部分分显然是n^2枚举 然后对于值域只有100我们每次直接查找下一个比$a_i$小的数字然后取模即可。
<p>通过这个做法我们可以发现取模的一些性质 对于x%y 如果y>x/2 那么x%y<x/2 如果y<x/2 那么x%y<x/2.
<p>这样我们发现每次x都会被去掉一半那么最多取模logn次就变成0了
<p>这样的话 我们每次查找<=x的数字即可 这样的操作最多logn次我们只要加快这个过程即可。
<p>值得一提的是有一个巧妙的做法 二分区间长度ST表查询最小值判定 这样就可以做到logn了而且也好写。
<p>但是却不支持修改(但是本题没有修改。直接在线段树上二分就很容易了 随着指针的向后移动我们把前面的都变成INF这样就保证了不会二分到前面这样也好写一点,当然可以不用这个操作不过要难写一点。
<p>关于线段树上二分的总结出门右转下一篇我总结了这个东西的写法,这里挂一个比较简单的写法。
//#include<bits/stdc++.h>
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
define INF 1000000001
define ll long long
define R register
define db double
define max(x,y) ((x)>(y)?(x):(y))
define min(x,y) ((x)>(y)?(y):(x))
define l(p) t[p].l
define r(p) t[p].r
define v(p) t[p].v
define zz p<<1
define yy p<<1|1
using namespace std;
char buf[1<<15],fs,ft;
inline char getc()
{
return (fsft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fsft))?0:fs++;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x10+ch-'0';ch=getchar();}
return xf;
}
const int MAXN=300010;
int n,mark,mark1;
int a[MAXN];
ll ans;
struct wy
{
int l,r;
int v;
}t[MAXN<<2];
inline void build(int p,int l,int r)
{
l(p)=l;r(p)=r;
if(lr){v(p)=a[l];return;}
int mid=(l+r)>>1;
build(zz,l,mid);
build(yy,mid+1,r);
v(p)=min(v(zz),v(yy));
}
inline void change(int p,int x)
{
if(l(p)r(p)){v(p)=INF;return;}
int mid=(l(p)+r(p))>>1;
if(x<=mid)change(zz,x);
else change(yy,x);
v(p)=min(v(zz),v(yy));
}
inline int ask(int p,int x)
{
if(l(p)r(p))return l(p);
if(v(p)>x)return n+1;
if(v(zz)<=x)return ask(zz,x);
return ask(yy,x);
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
n=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
if(a[i]>100)mark=1;
if(a[i]<a[i-1])mark1=1;//mark10时 不递减序列
}
if(n<=5000)//对于30分n^2暴力
{
for(int i=1;i<=n;++i)
{
int last=a[i];
ans+=a[i];
for(int j=i+1;j<=n;++j)
{
last=last%a[j];
ans+=last;
}
}
printf("%lld
",ans);
return 0;
}
if(!mark)//对于25分 nlogn100 30000010020
{
ans=0;
build(1,1,n);//线段树上二分
for(int i=1;i<=n;++i)
{
change(1,i);
ans+=a[i];
ll last=a[i];
int j=i+1;
while(j<=n)
{
int w=ask(1,last);
ans+=last(w-1-j+1);
if(w<=n)last=last%a[w];
j=w;
}
}
printf("%lld
",ans);
return 0;
}
if(!mark1)//对于15分 不递减序列
{
ans=0;
build(1,1,n);
for(int i=1;i<=n;++i)
{
change(1,i);
ans+=a[i];
int w=ask(1,a[i]);
if(w==n+1){ans+=(ll)a[i](ll)(n-i);}
else ans+=(ll)a[i](ll)(w-1-i);
}
printf("%lld
",ans);
return 0;
}
ans=0;
build(1,1,n);
for(int i=1;i<=n;++i)
{
change(1,i);
ans+=a[i];
ll last=a[i];
int j=i+1;
while(j<=n)
{
int w=ask(1,last);
ans+=last*(w-1-j+1);
if(w<=n)last=last%a[w];
j=w;
}
}
printf("%lld
",ans);
return 0;
}
革命尚未成功 我还是菜鸡。