[考试反思]0209省选模拟22:谬误

Posted hzoi-deepinc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[考试反思]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
",ans*R);
38 }
View Code
技术图片
 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 }
View Code

 

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
",ans);
30         for(int i=0;i<=T;++i)fir[i]=0;ec=1;ans=0;
31     }
32 }
View Code

 

以上是关于[考试反思]0209省选模拟22:谬误的主要内容,如果未能解决你的问题,请参考以下文章

[考试反思]0131省选模拟测14:遗失

[考试反思]0214省选模拟24:揣测

[考试反思]0220省选模拟27:怪异

[考试反思]0212省选模拟23:迷失

[考试反思]0110省选模拟5:信仰

[考试反思]0113省选模拟6:过载