两次考试
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"); } }
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("\n"); for(i=1;i<=n;++i) printf("%d",v[i]); printf("\n");*/ ans=1LL*n*(n+1)/2; for(i=0;i<3;++i) cm=i,ans-=get(); printf("%lld\n",ans); }
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\n",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\n",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\n",work()); } }
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\n",ans); }
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\n",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\n",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\n",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\n",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\n",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\n",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\n",work60()); else printf("%d\n",work100()); }
算法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\n",work()); }
算法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\n",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\n",work()); }
算法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\n",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\n",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\n",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\n",work()); }
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("\n"); for(i=0;i<n;++i) printf("%d ",t[i]); printf("\n");*/ 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("\n"); for(i=1;i<=sum;++i) printf("%d ",sun[i]); printf("\n");*/ for(i=1;i<=sum;++i) tt+=(sun[i]-1); //printf("arr=%d tt=%d\n",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\n",work()); }