两次考试
分类:
IT文章
•
2023-11-04 13:38:22
11.6
T1
super_gcd
还是要抓紧复习一下板子...
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define rint register int
using namespace std;
inline void read(int &x)
{
x=0; int ff=1; char q=getchar();
while(q<'0'||q>'9') { if(q=='-') ff=-1; q=getchar(); }
while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar();
x*=ff;
}
const int LEN=1006;
int t[LEN],he;
int cm;
struct bign
{
int s[LEN],len;
bign(){mem(s,0);len=1;}
void read()
{
int i;
he=0; char q=getchar();
while(q<'0'||q>'9') q=getchar();
while(q>='0'&&q<='9') t[++he]=q-'0',q=getchar();
for(i=he;i;--i) s[he-i+1]=t[i];
len=he;
}
bool operator < (const bign &c) const
{
bign x=*this;
if(x.len==c.len)
{
int i;
for(i=x.len;i;--i)
{
if(x.s[i]==c.s[i]) continue;
return x.s[i]<c.s[i];
}
}
else
return x.len<c.len;
return 0;
}
bign operator - (const bign &c) const
{
bign x=*this; int i;
for(i=1;i<=c.len;++i)
{
x.s[i+cm]-=c.s[i];
if(x.s[i+cm]<0)
{
--x.s[i+cm+1];
x.s[i+cm]+=10;
}
}
while(x.len>1&&x.s[x.len]==0)
--x.len;
return x;
}
void out()
{
int i;
for(i=len;i;--i)
printf("%d",s[i]);
puts("");
}
};
int T;
bign A,B;
int gcd()
{
while( !(A.len==1&&A.s[1]==0)&&!(B.len==1&&B.s[1]==0) )
{
if(A<B)
{
cm=B.len-A.len-1;
if(cm<0) cm=0;
B=B-A;
}
else
{
cm=A.len-B.len-1;
if(cm<0) cm=0;
A=A-B;
}
}
//A.out(); B.out();
if(A<B)
{
if(B.len==1&&B.s[1]==1&&A.len==1&&A.s[1]==0)
return 1;
return 0;
}
else
{
if(A.len==1&&A.s[1]==1&&B.len==1&&B.s[1]==0)
return 1;
return 0;
}
}
int main(){
//freopen("T1.in","r",stdin);
//freopen("king.in","r",stdin);
//freopen("king.out","w",stdout);
read(T);
while(T--)
{
A.read(); B.read();
//A.out(); B.out();
if(gcd())
puts("Yes");
else
puts("No");
}
}
T1
T2
考试什么都想不出来...
考虑补集转化,用总方案数减去0、1、2各自不合法的方案数
因为每个区间只会有一个数超过一半,所以不用容斥
枚举0、1、2
把跟它相等的数看做1,不相等的看做-1
这样统计前缀和中比当前低的位置即可
$O(n)$
其实$O(nlogn)$的很好想
$$pre_r-pre_{l-1}>frac{r-l+1}{2}$$
$$2*pre_r-r>2*pre_{l-1}-l+1$$
直接树状数组就行了
真是菜...
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define rint register int
using namespace std;
inline void read(int &x)
{
char q=getchar();
while(q<'0'||q>'9') q=getchar();
x=q-'0';
}
const int N=5000006;
int n;
int cm;
int aee=5000000;
int cnt[N<<1],v[N];
ll ans;
ll get()
{
rint i; int tt; ll ans=0,now=aee,ccc=0;
mem(cnt,0); ++cnt[aee];
for(i=1;i<=n;++i)
{
if(v[i]==cm) ccc+=cnt[now],++now;
else --now,ccc-=cnt[now];
++cnt[now];
ans+=ccc;
}
return ans;
}
int main(){
//freopen("ex1.in","r",stdin);
rint i;
scanf("%d",&n);
for(i=1;i<=n;++i) read(v[i]);
/*printf("
");
for(i=1;i<=n;++i)
printf("%d",v[i]);
printf("
");*/
ans=1LL*n*(n+1)/2;
for(i=0;i<3;++i)
cm=i,ans-=get();
printf("%lld
",ans);
}
T2
T3
$O(n^3)$的dp很简单,但是我根本没想...
考试打了用堆来贪心取,骗了40
70分 还可以打网络流
建图就S向豆干和干脆面连 流量为给出个数费用为0的边
豆干和干脆面在向每只猫连 一条$1/p_i$ 一条$1/0$ 的边
正解是wqs二分套wqs二分
二分两个cost,分别表示选豆干和干脆面各会需要额外的花费
每次dp记录最优的猫的个数和用掉的豆干和干脆面的个数
这样一直二分到第一个大于等于给出个数的值
而且二分出的值不一定可以使得dp结果正好是给出的个数
就像陈立杰那个黑白树一样(毕竟重打了7遍呢,记忆比较深刻...)
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define eps 0.00000001
#define dd double
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define rint register int
using namespace std;
inline void read(int &x)
{
x=0; int ff=1; char q=getchar();
while(q<'0'||q>'9') { if(q=='-') ff=-1; q=getchar(); }
while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar();
x*=ff;
}
const int N=100006;
const ll Inf=1e10;
int n,A,B;
int now,pr;
dd p[N],q[N];
dd f[2];
int ga[2],gb[2];
void check(dd xp,dd xq)
{
rint i,j;
now=0;
f[0]=0; ga[0]=gb[0]=0;
for(i=1;i<=n;++i)
{
now^=1; pr=now^1;
f[now]=-Inf; ga[now]=gb[now]=0;
if(f[now]<f[pr])
f[now]=f[pr],ga[now]=ga[pr],gb[now]=gb[pr];
if(f[now]<f[pr]+p[i]-xp)
f[now]=f[pr]+p[i]-xp,ga[now]=ga[pr]+1,gb[now]=gb[pr];
if(f[now]<f[pr]+q[i]-xq)
f[now]=f[pr]+q[i]-xq,ga[now]=ga[pr],gb[now]=gb[pr]+1;
if(f[now]<f[pr]+p[i]+q[i]-p[i]*q[i]-xp-xq)
f[now]=f[pr]+p[i]+q[i]-p[i]*q[i]-xp-xq,ga[now]=ga[pr]+1,gb[now]=gb[pr]+1;
}
}
dd work()
{
dd l=0,r=1,mid,l2,r2,mid2;
while(l<r-eps)
{
mid=(l+r)/2.0;
//printf("l=%f r=%f mid=%f
",l,r,mid);
l2=0; r2=1;
while(l2<r2-eps)
{
mid2=(l2+r2)/2.0;
//printf("l2=%f r2=%f mid2=%f gb[now]=%d
",l2,r2,mid2,gb[now]);
check(mid,mid2);
if(gb[now]==B)
break;
if(gb[now]<B) r2=mid2;
else l2=mid2;
}
if(ga[now]==A)
break;
if(ga[now]<A) r=mid;
else l=mid;
}
return f[now]+mid*A+mid2*B;
}
int main(){
//freopen("in.in","r",stdin);
//freopen("red8.in","r",stdin);
rint i;
while(~scanf("%d%d%d",&n,&A,&B))
{
for(i=1;i<=n;++i) scanf("%lf",&p[i]);
for(i=1;i<=n;++i) scanf("%lf",&q[i]);
if(A>n) A=n;
if(B>n) B=n;
printf("%.3f
",work());
}
}
T3
11.7
T1
T一定可以分解成$S*B^{a}+n1*A*B^{a-1}+n2*A*B^{a-2}...$的形式
其中n1,n2是系数,可以为0
这样的话,直接枚举a,在贪心取就行了
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define rint register int
using namespace std;
inline void read(int &x)
{
x=0; int ff=1; char q=getchar();
while(q<'0'||q>'9') { if(q=='-') ff=-1; q=getchar(); }
while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar();
x*=ff;
}
int S,T,A,B;
int maxk;
ll P[106];
int main(){
//freopen("T1.in","r",stdin);
//freopen("T1hh.out","w",stdout);
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
rint i,j; ll tt,t1; int con,ans=1e9;
read(S); read(T); read(A); read(B);
if(S>T)
{
puts("-1");
return 0;
}
tt=S; maxk=0;
while(tt<=T&&maxk<=32) tt*=B,++maxk;
--maxk; tt/=B;
P[0]=1;
for(i=1;i<=maxk;++i)
P[i]=P[i-1]*B;
for(i=maxk;~i;--i)
{
tt=S*P[i]; con=i;
for(j=i;~j;--j)
{
t1=(1LL*T-tt)/(1LL*A*P[j]);
con+=t1; tt+=1LL*t1*A*P[j];
}
if(tt==T&&ans>con) ans=con;
}
if(ans==1000000000)
ans=-1;
printf("%d
",ans);
}
T1
T2
算法1(考试骗分) 90
先考虑二分ans
然后二分点对的左端点距离选择的边的左端点的最大值
然后对右端点做区间交
$O(nlog^2n)$
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define rint register int
using namespace std;
inline void read(int &x)
{
x=0; int ff=1; char q=getchar();
while(q<'0'||q>'9') { if(q=='-') ff=-1; q=getchar(); }
while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar();
x*=ff;
}
const int N=100006;
int n,m;
int u[N],v[N],len[N];
int check60(int maxd)
{
rint i,j; int mn,mx,t1,t2;
for(i=1;i<=n;++i)
{
mn=1000000; mx=1;
for(j=1;j<=m;++j)
if(len[j]>maxd)
{
t1=u[j]-i; if(t1<0) t1=-t1;
if(t1>maxd)
{
mn=1; mx=1000000;
break;
}
t2=maxd-t1;
if(mn>v[j]+t2) mn=v[j]+t2;
if(mx<v[j]-t2) mx=v[j]-t2;
}
if(mx<=mn) return 1;
}
return 0;
}
int work60()
{
int l=0,r=n,mid,ans=n;
while(l<=r)
{
mid=(l+r)>>1;
//printf("l=%d r=%d mid=%d
",l,r,mid);
if(check60(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
int check100(int maxd)
{
int l=0,r=maxd,mid;
int mn,mx,t1,t2,tt,l1=n,r1=1;
rint i,j;
while(l<=r)
{
mid=(l+r)>>1;
//printf("l2=%d r2=%d mid2=%d
",l,r,mid);
mn=n; mx=1;
for(i=1;i<=m;++i)
if(len[i]>maxd)
{
if(mx<u[i]-mid) mx=u[i]-mid;
if(mn>u[i]+mid) mn=u[i]+mid;
}
//printf("mn=%d mx=%d
",mn,mx);
if(mx>mn) l=mid+1;
else
{
l1=mx,r1=mn,r=mid-1;
/*if(l1>mx) l1=mx;
if(r1<mn) r1=mn;
r=mid-1;*/
/*if(r1-l1<mn-mx)
l1=mx,r1=mn;
r=mid-1;*/
}
}
if(l1>r1) return 0;
//printf("l1=%d r1=%d mid=%d
",l1,r1,mid);
for(i=l1;i<=r1;++i)
{
t1=n; t2=1;
for(j=1;j<=m;++j)
if(len[j]>maxd)
{
tt=u[j]-i; if(tt<0) tt=-tt;
tt=maxd-tt;
if(t2<v[j]-tt) t2=v[j]-tt;
if(t1>v[j]+tt) t1=v[j]+tt;
}
//printf("asdsd:: %d %d
",t2,t1);
if(t2<=t1) return 1;
}
return 0;
}
int work100()
{
int l=0,r=n,mid,ans=n;
while(l<=r)
{
mid=(l+r)>>1;
//printf("l=%d r=%d mid=%d
",l,r,mid);
if(check100(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
int main(){
//freopen("T2.in","r",stdin);
//freopen("T2.out","w",stdout);
//freopen("b.in","r",stdin);
//freopen("b.out","w",stdout);
rint i;
read(n); read(m);
for(i=1;i<=m;++i)
read(u[i]),read(v[i]),len[i]=v[i]-u[i];
if(n<=5000)
printf("%d
",work60());
else
printf("%d
",work100());
}
1
算法2 100
其实左端点并不具有二分性质,而是一个横坐标为左端点pos,竖坐标为ans的下凹函数
那么可以三分套二分
$O(nlog^2n)$
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define rint register int
using namespace std;
inline void read(int &x)
{
x=0; int ff=1; char q=getchar();
while(q<'0'||q>'9') { if(q=='-') ff=-1; q=getchar(); }
while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar();
x*=ff;
}
const int N=100006;
int n,m;
int u[N],v[N],len[N];
int check2(int pos,int maxd)
{
rint i; int l1=1,r1=n,t1;
for(i=1;i<=m;++i)
if(len[i]>maxd)
{
t1=u[i]-pos; if(t1<0) t1=-t1;
if(t1>maxd) return 0;
}
for(i=1;i<=m;++i)
if(len[i]>maxd)
{
t1=u[i]-pos; if(t1<0) t1=-t1;
t1=maxd-t1;
if(l1<v[i]-t1) l1=v[i]-t1;
if(r1>v[i]+t1) r1=v[i]+t1;
}
if(l1<=r1) return 1;
return 0;
}
int check(int pos)
{
rint i;
int l=0,r=n,mid,ans=n;
while(l<=r)
{
mid=(l+r)>>1;
if(check2(pos,mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
int work()
{
int l=1,r=n,mid,midmid,midv,midmidv,ans=n,i;
while(l<=r-10)
{
mid=l+(r-l)/3; midmid=r-(r-l)/3;
midv=check(mid); midmidv=check(midmid);
if(midv>midmidv)
{
if(ans>midmidv) ans=midmidv;
l=mid;
}
else
{
if(ans>midv) ans=midv;
r=midmid;
}
}
for(i=l;i<=r;++i)
{
midv=check(i);
if(ans>midv) ans=midv;
}
return ans;
}
int main(){
//freopen("T2.in","r",stdin);
rint i;
read(n); read(m);
for(i=1;i<=m;++i)
read(u[i]),read(v[i]),len[i]=v[i]-u[i];
printf("%d
",work());
}
2
算法3 100
二分ans
对于每一个点对,发现合法的区间映射到二维平面是其实是一个个倾斜45度的正方形
用$(x+y,x-y)$把正方形旋转成水平的(旋转过程大小会变,但是相对位置不会)
这样矩形求交就行了
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define rint register int
using namespace std;
inline void read(int &x)
{
x=0; int ff=1; char q=getchar();
while(q<'0'||q>'9') { if(q=='-') ff=-1; q=getchar(); }
while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar();
x*=ff;
}
const int N=100006;
const int Inf=1e9;
int n,m;
int u[N],v[N],len[N];
int check(int maxd)
{
rint i;
int l=-Inf,r=Inf,xi=-Inf,sh=Inf;
int t1,t2;
int sha,xia,zuo,you;
for(i=1;i<=m;++i)
if(len[i]>maxd)
{
sha=-Inf; xia=Inf; zuo=Inf; you=-Inf;
t1=u[i]-maxd+v[i]; t2=u[i]-maxd-v[i];
if(sha<t2) sha=t2; if(xia>t2) xia=t2;
if(you<t1) you=t1; if(zuo>t1) zuo=t1;
t1=u[i]+maxd+v[i]; t2=u[i]+maxd-v[i];
if(sha<t2) sha=t2; if(xia>t2) xia=t2;
if(you<t1) you=t1; if(zuo>t1) zuo=t1;
t1=u[i]+v[i]-maxd; t2=u[i]-(v[i]-maxd);
if(sha<t2) sha=t2; if(xia>t2) xia=t2;
if(you<t1) you=t1; if(zuo>t1) zuo=t1;
t1=u[i]+v[i]+maxd; t2=u[i]-(v[i]+maxd);
if(sha<t2) sha=t2; if(xia>t2) xia=t2;
if(you<t1) you=t1; if(zuo>t1) zuo=t1;
if(l<zuo) l=zuo; if(r>you) r=you;
if(xi<xia) xi=xia; if(sh>sha) sh=sha;
}
if(xi<=sh&&l<=r)
return 1;
return 0;
}
int work()
{
int l=0,r=n,mid,ans=n;
while(l<=r)
{
mid=(l+r)>>1;
//printf("l=%d r=%d mid=%d
",l,r,mid);
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
int main(){
//freopen("T2.in","r",stdin);
//freopen("T2.out","w",stdout);
rint i;
read(n); read(m);
for(i=1;i<=m;++i)
read(u[i]),read(v[i]),len[i]=v[i]-u[i];
printf("%d
",work());
}
3
算法4 100
其实算法3的check就是求曼哈顿距离可否满足maxd
那么把曼哈顿距离转化成切比雪夫距离
这样横纵坐标就独立起来了
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define rint register int
using namespace std;
inline void read(int &x)
{
x=0; int ff=1; char q=getchar();
while(q<'0'||q>'9') { if(q=='-') ff=-1; q=getchar(); }
while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar();
x*=ff;
}
const int Inf=1e9;
const int N=100006;
int n,m;
int u[N],v[N],len[N];
int check(int maxd)
{
rint i; int l=-Inf,r=Inf;
for(i=1;i<=m;++i)
if(len[i]>maxd)
{
if(l<u[i]+v[i]-maxd) l=u[i]+v[i]-maxd;
if(r>u[i]+v[i]+maxd) r=u[i]+v[i]+maxd;
}
//printf("l1=%d r1=%d
",l,r);
if(l>r) return 0;
l=-Inf; r=Inf;
for(i=1;i<=m;++i)
if(len[i]>maxd)
{
if(l<u[i]-v[i]-maxd) l=u[i]-v[i]-maxd;
if(r>u[i]-v[i]+maxd) r=u[i]-v[i]+maxd;
}
//printf("l2=%d r2=%d
",l,r);
if(l>r) return 0;
return 1;
}
int work()
{
int l=0,r=n,mid,ans=n;
while(l<=r)
{
mid=(l+r)>>1;
//printf("l=%d r=%d mid=%d
",l,r,mid);
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
int main(){
// freopen("T2.in","r",stdin);
rint i;
read(n); read(m);
for(i=1;i<=m;++i)
read(u[i]),read(v[i]),len[i]=v[i]-u[i];
printf("%d
",work());
}
4
T3
结论:
不管对手怎么动,只要预先知道了他的行动
就可以对应的进行行动,总有一种方案使得最后得到结果
二分即可
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define rint register int
using namespace std;
inline void read(int &x)
{
x=0; int ff=1; char q=getchar();
while(q<'0'||q>'9') { if(q=='-') ff=-1; q=getchar(); }
while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar();
x*=ff;
}
const int N=300006;
int first[N],nt[N<<1],ver[N<<1],e;
void addb(int u,int v)
{
ver[e]=v;
nt[e]=first[u];
first[u]=e++;
}
int n,alln;
int v[N],t[N],pos[N];
int x[N<<1],y[N<<1];
int dfn[N],low[N],tim,zhan[N],he,sun[N],sum;
bool flag[N];
void tarjan(int x)
{
dfn[x]=low[x]=++tim;
zhan[++he]=x; flag[x]=1;
int i;
for(i=first[x];i!=-1;i=nt[i])
{
if(dfn[ver[i]]==-1)
{
tarjan(ver[i]);
if(low[x]>low[ver[i]])
low[x]=low[ver[i]];
}
else
if(flag[ver[i]]&&low[x]>dfn[ver[i]])
low[x]=dfn[ver[i]];
}
if(dfn[x]==low[x])
{
i=-1; ++sum;
while(i!=x)
i=zhan[he--],flag[i]=0,++sun[sum];
}
}
int check(int arr)
{
rint i; int tt;
for(i=0;i<n;++i) t[i]=v[i];
for(i=1;i<=arr;++i) swap(t[x[i]],t[y[i]]);
/*printf("
");
for(i=0;i<n;++i)
printf("%d ",t[i]);
printf("
");*/
for(i=0;i<n;++i) pos[t[i]]=i;
for(i=0;i<n;++i) sun[i]=0,dfn[i]=-1,first[i]=-1;
e=0; tim=0; he=0; sum=0;
for(i=0;i<n;++i) addb(i,pos[i]);
for(i=0;i<n;++i)
if(dfn[i]==-1)
tarjan(i);
tt=0;
/*printf("
");
for(i=1;i<=sum;++i)
printf("%d ",sun[i]);
printf("
");*/
for(i=1;i<=sum;++i)
tt+=(sun[i]-1);
//printf("arr=%d tt=%d
",arr,tt);
return tt<=arr;
}
int work()
{
int l=0,r=alln,mid,ans=alln;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
int main(){
//freopen("T3.in","r",stdin);
rint i;
read(n); alln=n<<1;
for(i=0;i<n;++i)
read(v[i]);
for(i=1;i<=alln;++i)
read(x[i]),read(y[i]);
printf("%d
",work());
}
T3