[考试反思]0209省选模拟22:谬误
感觉自己被上天嘲讽了一样。。。联赛后拿的好名次都是通过一些奇奇怪怪的原因。
上次是毒瘤打表被我碰上了,这次是数据水乱打能拿好多分(刚开始)
但是大体也还行后两题都写对拍了,还都发现锅了
只不过当时没有好好学manacher导致考场上并没有打出来,所以就只写了暴力。
然后也就没啥了,对拍都挂上了,也就没啥说的
只不过T1高的出乎意料了,于是找教练加了两三组数据把自己$hack$掉。
应该像$NC$说的那样加个20组的这样我就能大概看到真正的名次了。T1其实得10分也就差不多了。
然而自己加的几组数据其实还不够毒瘤。。。可以hack一部分正解。。。
T1:遮天蔽日
大意:点光源照圆,中间有个不透明多边形,多边形会绕重心自转,绕圆形公转,给定时刻,求圆被照射到的弧长。
题目的本意是好的,但是写起来是真的恶心。
看着$std$大概写了下来,其实很好理解,也并没有想象中那么恶心。
首先先按照题意公转自转。先后顺序无所谓,但是共转并不能让所有点都公转,而是让重心公转,点的相对位置不变。
问题是求重心:类似加权平均数的样子,所有点向原点连线,然后做一个带正负的三角剖分,重心坐标即为两点坐标按照面积加权后/3。
skyh说的:感性理解+记个板子足够用了。
然后所有点与光源连线,再连上俩切线,这些直线按照极角排序。
为了方便,我们设靠下的直线的极角为基准值0,其余均与之做差。先把范围外的都干掉。
然后现在讨论的就是两条切线之间的线。
对于每两条线之间的部分,这段区域要么全亮要么全不亮,所以我们从中任选一个角度。
暴力枚举多边形所有边,用向量判断是否有交点即可。
计算切线用到了正弦定理。最后算弧长的时候,实际上为了方便,我们求的是0~x这段角度所对应的圆心角大小。
然后询问某一段的时候相当于一个差分。
具体计算这个值的话,是点与圆心连线后再去用正弦定理计算圆心角。
只需要一个完整的向量桶,然后就按照直觉来。
代码写出二义性了,调到暴毙,原理不明。
我写的这个不会被凹多边形$hack$。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const double pi=3.1415926535,Pi=2*pi; 4 struct P{ 5 double x,y; 6 P():x(0),y(0){} 7 P(double X,double Y):x(X),y(Y){} 8 P operator+(P b){return P(x+b.x,y+b.y);} P&operator+=(P b){x+=b.x;y+=b.y;} 9 P operator-(P b){return P(x-b.x,y-b.y);} P&operator-=(P b){x-=b.x;y-=b.y;} 10 P operator*(double b){return P(x*b,y*b);} P&operator*=(double b){x*=b;y*=b;} 11 P operator/(double b){return P(x/b,y/b);} P&operator/=(double b){x/=b;y/=b;} 12 double operator*(P b){return x*b.x+y*b.y;} double operator^(P b){return x*b.y-b.x*y;} 13 double len(){return sqrt(x*x+y*y);} double A(){return atan2(y,x);} 14 P spin(double A){return P(x*cos(A)-y*sin(A),x*sin(A)+y*cos(A));} 15 }S,E,p[33],G; 16 int n,Ac;double t1,t2,t,R,area,lenSE,dA,lwr,A[33],ans; 17 double cal(double a){double A=(a-dA/2);return asin(max(-1.,min(1.,sin(A)/R*lenSE)))-A;} 18 int main(){ 19 cin>>S.x>>S.y>>E.x>>E.y>>R>>n>>t1>>t2>>t; 20 lenSE=(E-S).len(); dA=asin(R/lenSE)*2; lwr=(E-S).A()-dA/2; 21 if(lwr<-pi)lwr+=Pi; if(lwr>pi)lwr-=Pi; 22 for(int i=0;i<n;++i)cin>>p[i].x>>p[i].y; 23 for(int i=0;i<n;++i)G+=(p[i]+p[(i+1)%n])*(p[i]^p[(i+1)%n]),area+=p[i]^p[(i+1)%n]; 24 G/=3*area;for(int i=0;i<n;++i)p[i]-=G; 25 t1=t/t1*Pi;t2=t/t2*Pi; 26 G=E+(G-E).spin(t1); for(int i=0;i<n;++i)p[i]=G+p[i].spin(t2); 27 A[++Ac]=0; A[++Ac]=dA; 28 for(int i=0;i<n;++i)A[++Ac]=(p[i]-S).A()-lwr; 29 for(int i=1;i<=Ac;++i)if(A[i]<-pi)A[i]+=Pi;else if(A[i]>pi)A[i]-=Pi; 30 sort(A+1,A+1+Ac); 31 for(int i=1,ok;ok=1,i<Ac;++i)if(A[i]>=0&&A[i]<dA&&A[i+1]-A[i]>1e-7){ 32 P tmp=P(cos((A[i+1]+A[i])/2+lwr),sin((A[i+1]+A[i])/2+lwr)),x,y; 33 for(int j=0;j<n;++j)if((((x=p[j])-S)^tmp)*(((y=p[(j+1)%n])-S)^tmp)<0){ 34 y-=x;double rate=(y^(S-x))/(tmp^y); 35 if(rate>0&&(S+tmp*rate-E)*(S-E)>0){ok=0;break;} 36 }if(ok)ans+=cal(A[i+1])-cal(A[i]); 37 }printf("%.2lf\n",ans*R); 38 }
1 0 0 100 0 5 2 12 1 1 0 3 90 1 4 90 100 5 110 100 6 110 -100 7 90 -100 8 90 -1 9 89 -4 10 89 -111 11 111 -111 12 111 111 13 89 111 14 89 4
T2:三元组
大意:给定字符串求所有满足$i \le j < k$且$S(i,j),S(j+1,k)$都是回文串的$\sum ik$。多测。$|S| \le 10^6, T \le 5$
很明显在卡$O(nlogn)$。
线性回文串就$manacher$。需要维护以点$i$为左端点的回文串的右端点编号和。区间加等差数列。
问题是离线的,也就是询问都在修改后。维护两个数组$a,b$表示位置$i$往后要加上以$b[i]$为首项$a[i]$为公差的等差数列。
最后做前缀和就行了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 2222222 4 #define mod 1000000007 5 char s[S];int R[S],C,mR=-1,ans,a[S],b[S],c[S],d[S]; 6 void add(int&x,int y){x+=y;if(x<0)x+=mod;if(x>=mod)x-=mod;} 7 int main(){s[0]='_';//freopen("3.in","r",stdin);freopen("3.out","w",stdout); 8 int t;cin>>t;while(t-->0){ 9 scanf("%s",s+1);int n=strlen(s+1);mR=-1; 10 for(int i=n;i;--i)s[i<<1]=s[i],s[i]=0; 11 for(int i=1;i<n+1<<1;++i){ 12 R[i]=i<=mR?min(mR-i+1,R[2*C-i]):0; 13 while(s[i+R[i]]==s[i-R[i]])R[i]++; 14 if(i+R[i]>mR)mR=i+R[i]-1,C=i; 15 } 16 for(int i=1;i<n+1<<1;++i)R[i]--; 17 for(int i=1;i<n+1<<1;++i){ 18 int l=(i-R[i]>>1)+1,r=i+R[i]>>1,md=l+r>>1; 19 if(r>=l)a[md+(i&1)]--,a[r+1]++,add(b[md+(i&1)],md),add(b[r+1],1-l); 20 } 21 for(int i=1;i<=n;++i)c[i]=b[i],add(b[i+1],b[i]+a[i]),a[i+1]+=a[i],a[i]=b[i]=0; 22 for(int i=1;i<n+1<<1;++i){ 23 int l=(i-R[i]>>1)+1,r=i+R[i]>>1,md=l+r>>1; 24 if(r>=l)a[l]--,a[md+1]++,add(b[l],r),add(b[md+1],1-md-(i&1)); 25 } 26 for(int i=1;i<=n;++i)d[i]=b[i],add(b[i+1],b[i]+a[i]),a[i+1]+=a[i],a[i]=b[i]=0; 27 ans=0; for(int i=1;i<n;++i)ans=(ans+1ll*c[i]*d[i+1])%mod; cout<<ans<<endl; 28 } 29 }
T3:最有价值
大意:数字串,选中一个子序列,对于数字$i$若选中了则$b_i$代价,每多选一次额外付出$a_i$。位置$i,j$同时选收益$w_{i,j}$。最大化收益。$n \le 100, T \le 20$
同时选有收益,非常经典的网络流模型。
处理每种数字的贡献,只需要新建$10$个点表示数字,连$a_i-b_i$的边表示选这种数有这样的代价,再有这10个点向序列对应位置连边。
可以用老方法讲点数优化至$O(n)$但是没啥必要,就这样吧。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 66666 4 #define inf 998244353 5 int n,ec=1,t,fir[S],l[S],to[S],v[S],T=6666,w[111][111],a[11],pc,b[11],ans,d[S],q[S];char s[111]; 6 void link(int a,int b,int w){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=w;} 7 void con(int a,int b,int w){link(a,b,w);link(b,a,0);} 8 bool bfs(){ 9 for(int i=1;i<=T;++i)d[to[i]]=0;d[0]=1; 10 for(int h=1,t=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i])if(!d[to[i]]&&v[i])d[q[++t]=to[i]]=d[q[h]]+1; 11 return d[T]; 12 } 13 int dfs(int p,int f){ 14 if(p==T)return f;int r=f; 15 for(int i=fir[p];i&&r;i=l[i])if(d[to[i]]==d[p]+1&&v[i]){ 16 int x=dfs(to[i],min(v[i],r)); 17 if(!x)d[to[i]]=0; 18 v[i]-=x;v[i^1]+=x;r-=x; 19 }return f-r; 20 } 21 int main(){ 22 // freopen("3.in","r",stdin);freopen("3.out","w",stdout); 23 cin>>t;while(t-->0){ 24 scanf("%d%s",&n,s+1);pc=n+10; 25 for(int i=0;i<=9;++i)scanf("%d%d",&a[i],&b[i]),con(0,n+i+1,b[i]-a[i]); 26 for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)scanf("%d",&w[i][j]),ans+=w[i][j]; 27 for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j)con(i,++pc,inf),con(j,pc,inf),con(pc,T,w[i][j]+w[j][i]); 28 for(int i=1;i<=n;++i)con(0,i,a[s[i]-48]),con(n+s[i]-47,i,inf),ans-=w[i][i]; 29 while(bfs())ans-=dfs(0,inf);printf("%d\n",ans); 30 for(int i=0;i<=T;++i)fir[i]=0;ec=1;ans=0; 31 } 32 }