多校联考7
rating 29 ,mark 210 骄兵必败
题纲:T1:暴力+优化
T2:计算几何+对称转化-->回文字符串
T3:莫比乌斯反演balabala...
T4:二分图边色数-VIking定理(或者说树图简单模拟找规律)
T1:
给你n个四元组,分为k类,每类只能选择一个,ans=(sigma(ai)+100)(sigma(bi)+100)(sigma(ci)+100)
*(sigma(di)+100)求ans的最大值 n<=50 k<=50
暴力的优化:
一般是DFS,会在dfs过程中建立一颗搜索树,搜索树的节点数决定了搜索次数也就是时间复杂度
恰当的搜索顺序和剪枝技巧可以优秀地减小时间复杂度
(1)不合法位置的跳跃
如果cot[ki]=0,如果dfs一般都是
if(cot[ki]==0)dfs(ki+1,a,b,c,d)
但是假如有连续的一串cot[ki]=0那么就会在每一个递归到这个节点的·地方多出来一条链,
如果刚好是在比较靠下的搜索树位置,时间复杂度就会退化很厉害
所以应该写成
if(cot[ki]==0)dfs(nxt[ki],a,b,c,d)
这个nxt可以预处理一下,一定要倒着更新!!
int n,k;
int g[60][60],cot[60];//存每种部位有多少装备
bool vis[60][60];
vector<int>gia[60];
int a[60],b[60],c[60],d[60];//存装备属性
ll ans;
int ya[60],yb[60],yc[60],yd[60];//选到当前状态的最大值
bool has;
int nxt[60];
void dfs(int x,ll ar,ll br,ll cr,ll dr)
{
if(x>k)
{
ans=max(ans,(ar+100)*(br+100)*(cr+100)*(dr+100));return;
}
int siz=cot[x];
if(!siz)
{
dfs(nxt[x],ar,br,cr,dr);return;
}
//if((ar))
if(ar>ya[x-1]&&br>yb[x-1]&&cr>yc[x-1]&&dr>yd[x-1])
{
ya[x-1]=ar,yb[x-1]=br;yc[x-1]=cr;yd[x-1]=dr;has=1;
}
else if(has&&ar<=ya[x-1]&&br<=yb[x-1]&&cr<=yc[x-1]&&dr<=yd[x-1])return;//没必要找了
for(rint i=1;i<=cot[x];++i)
{
dfs(x+1,ar+a[g[x][i]],br+b[g[x][i]],cr+c[g[x][i]],dr+d[g[x][i]]);
}
}
int main()
{
//freopen("calc.in","r",stdin);
//freopen("calc.out","w",stdout);
n=re(),k=re();
_f(i,1,n)
{
int tp=re();a[i]=re(),b[i]=re(),c[i]=re(),d[i]=re();
gia[tp].push_back(i);
}
_f(i,1,k)
{
int siz=gia[i].size();
if(!siz)continue;
_f(j,0,siz-1)
{
_f(k,j+1,siz-1)//每一对都比较
{
if(a[gia[i][j]]<=a[gia[i][k]]&&b[gia[i][j]]<=b[gia[i][k]]&&c[gia[i][j]]<=c[gia[i][k]]&&d[gia[i][j]]<=d[gia[i][k]])vis[i][j]=1;//不要一个
else if(a[gia[i][j]]>=a[gia[i][k]]&&b[gia[i][j]]>=b[gia[i][k]]&&c[gia[i][j]]>=c[gia[i][k]]&&d[gia[i][j]]>=d[gia[i][k]])vis[i][k]=1;//不要一个
}
}
_f(j,0,siz-1)
{
if(!vis[i][j])g[i][++cot[i]]=gia[i][j];//可以要
//,chu("g[%d][%d]=%d\n",i,cot[i],gia[i][j])
}
}
T2
给你一个n边形,求这个n边形的对称轴数量
其实思路...
(1)暴力搜索,直接枚举对称轴由哪些点对构成,O(n)判断是否合法,但是实现的过程很重要,比如判断a点和b点关于直线l对称,可以直接从直线上取两个点算a,b到两个点的距离相同,这个很好实现,但是如果你用什么k1*k2=-1判断垂直,就会很麻烦而且会出现/0的边界,所以一定要善于多思考,简化问题
const double eps=1e-6;//精度误差
int T,n;
double x[100000+10],y[100000+10];//n=1?n=2?
int id[300000+10];
inline double dis(double xi,double yi,double xj,double yj)
{
return (xi-xj)*(xi-xj)+(yi-yj)*(yi-yj);
}
inline bool cmp(double afx,double afy,double btx,double bty,double cx,double cy,double dx,double dy)
{
if(fabs(dis(afx,afy,cx,cy)-dis(btx,bty,cx,cy))<=eps&&fabs(dis(afx,afy,dx,dy)-dis(btx,bty,dx,dy))<=eps )return 1;
return false;
}
inline void deal_jishu()
{
int nas=0;
int ed=(n>>1);
for(rint i=1;i<=n;++i)//每个点作为对称轴的一部分尝试
{
int tp=i,lp=id[i+ed],rp=id[i+ed+1];int bj=(n-3)/2;
//chu("%d--(%d,%d)\n",i,lp,rp);
bool sc=1;
for(rint len=1;len<=bj;++len)
{
int lt=id[i+len],rt=id[i+n-len];
//chu("is %d %d secood\n",lt,rt);
if(!cmp(x[lt],y[lt],x[rt],y[rt],x[i],y[i],(x[lp]+x[rp])/2,(y[lp]+y[rp]/2)))
{
//chu("npo\n");
sc=0;break;
}
//chu("yes\n");
}
if(sc==1)++nas;
}
chu("%d\n",nas);
}
inline void deal_oushu()
{
int nas=0;
int ed=(n>>1);
for(rint i=1;i<=ed;++i)//枚举对点
{
int ip=i+ed;//i,ip
// chu("%d and %d lian\n",i,ip);
bool sucd=1;//是不是
for(rint len=1;len<=ed-1;++len)//枚举对称点离中心距离
{
int lp=id[i+n-len],rp=id[i+len];
if(!cmp(x[lp],y[lp],x[rp],y[rp],x[i],y[i],x[ip],y[ip]))
{
sucd=0;break;
}
}
if(sucd)++nas;
}
// chu("%d\n",nas);
for(rint i=1;i<=ed;++i)
{
int _i=id[i+1];
int ip=id[i+ed],_ip=id[ip+1];//i-i+1 ip-ip+1是对称轴
bool sucd=1;
//chu("try:%d %d,%d %d\n",i,_i,ip,_ip);
for(rint len=0;len<=ed-1;++len)
{
int lp=id[i+n-len],rp=id[i+1+len];
if(!cmp(x[lp],y[lp],x[rp],y[rp],(x[i]+x[_i])/2,(y[i]+y[_i])/2,(x[ip]+x[_ip])/2,(y[ip]+y[_ip])/2))
{
sucd=0;break;
}
//chu("can\n");
}
if(sucd)nas++;
}
chu("%d\n",nas);
}//还要垂直才能对称,中点还不太行
int main()
{
// freopen("calc.in","r",stdin);
//freopen("calc.out","w",stdout);
//freopen("symmetry2.in","r",stdin);
T=re();
while(T--)
{
n=re();
_f(i,1,n)id[i]=i;
_f(i,n+1,n+n)id[i]=id[i-n];
_f(i,1,n)
{
scanf("%lf%lf",&x[i],&y[i]);
}//怎么给都一样
//chu("4");
if(!(n&1))deal_oushu();
else deal_jishu();
}
return 0;
}
80tps
感谢icecup大佬的鼎力相助,100tps HERE!
其实100分就是把轴对称转化成对于不同的角度和边能够在展开成环的时候变成一段回文,点点对称是中心对称回文,边边对称是轴对称回文,这样manacher或者KMP就可以O(n)扫出整体出现的回文串了|||现在考虑怎么唯一表示出多边形夹角和边长度,把边看成向量,点乘+叉乘可以唯一对应一个角度(sin+cos),和长度(|a||b|),所以算一下这两个量就可以表示出多边形了(hash思想) KMP:用倒着的s匹配一遍,出现的匹配串就是回文串的一半(另一半一定在前面出现,就是前缀)
ll s[800000+10];//len=n*2=
ll man[800000+10];
ll x[200000+10],y[200000+10];
int id[400000+10];
int pos[1600000+10];
int n;
inline ll Dis(ll xi,ll yi,ll xj,ll yj)
{
return ((ll)xi-(ll)xj)*((ll)xi-(ll)xj)+((ll)yi-(ll)yj)*((ll)yi-(ll)yj);
}
int main()
{
// freopen("calc.in","r",stdin);
//freopen("calc.out","w",stdout);
//freopen("symmetry2.in","r",stdin);
int T=re();
while(T--)
{
n=re();
_f(i,1,n)
scanf("%lld %lld",&x[i],&y[i]);
int cp=n;
_f(i,1,n)id[i]=i,id[i+n]=i;
_f(i,1,n)//依次算dis
{
s[i]=((ll)x[id[i+1]]-x[id[i]])*(x[id[i+2]]-x[id[i+1]])+(ll)(y[id[i+1]]-y[id[i]])*(y[id[i+2]]-y[id[i+1]])+(ll)(x[id[i+1]]-x[id[i]])*(y[id[i+2]]-y[id[i+1]])-(ll)(y[id[i+1]]-y[id[i]])*(x[id[i+2]]-x[id[i+1]]);
}
//chu("\n");
for(rint i=1;i<=n;++i)
{
s[i+n]=s[i];
// chu("%lld ",s[i]);
}
//chu("\n");
n<<=1;
for(rint i=1;i<=n;++i)
{
man[(i<<1)-1]=s[i];
man[i<<1]=0;//距离会不会更大?但我这个是小!但是角度取负数的话...
}
//然后是manancher
n<<=1;
n-=1;
int nas=0;
int R=0,mid=0;
for(rint i=1;i<=n;++i)
{
if(i<R)pos[i]=min(R-i+1,pos[2*mid-i]);
else pos[i]=1;
//chu("pos[%d]:%d\n",i,pos[i]);
while((i-pos[i])>=1&&(i+pos[i])<=n&&man[i-pos[i]]==man[i+pos[i]])++pos[i];
if(i+pos[i]-1>R)
{
R=i+pos[i]-1;
mid=i;
}
if(pos[i]>=cp)nas++//这里是不严格!!!;
// chu("pos[(%lld)%d:%d\n",man[i],i,pos[i]);
}
chu("%d\n",nas/2);//是因为每个都会算两次??
}
return 0;
}