B. Boundary
题意:给定n个点的点坐标,求所有过(0,0)的圆中,圆上能覆盖最多给定点的圆。输出覆盖点的最大个数。
分析:
n=1,那么答案就是1.
n>1,因为三个点能确定一个圆,n^2枚举两个点与原点连成三角形,求三角形的外心,然后存储坐标。这样我们可以枚举c出所有覆盖点>=2的圆。(求三角形外心直接百度板子
由于这道题卡常数,不能用map计数,我们可以sort一遍,然后遍历计算圆心的出现次数。求得最大值 .
假如一个圆覆盖了 个点,那么对于
个点两两组合,重复计数圆心次数有
次。所以最后计算答案还要再转换一下。
#include<bits/stdc++.h>
using namespace std;
typedef double db;
struct point{
db x,y;
point(){}
point( db _x,db _y ):x(_x),y(_y){ }
db operator * ( const point &rhs ) const { return 1ll*x*rhs.y-1ll*y*rhs.x; }
point operator - ( const point &rhs ) const { return point(x-rhs.x,y-rhs.y); }
}a[2005];
db X,Y;
vector< pair<db,db> > p;
void solve( point x,point y,point z )
{
db x1=x.x,x2=y.x,x3=z.x;
db y1=x.y,y2=y.y,y3=z.y;
if( (x-y)*(y-z)==0 ) X=1e18,Y=1e18;
else
{
db G=(y3-y2)*x1+(y1-y3)*x2+(y2-y1)*x3;
db A=x1*x1+y1*y1,B=x2*x2+y2*y2,C=x3*x3+y3*y3;
X=( (B-C)*y1+(C-A)*y2+(A-B)*y3 )/(2*G);
Y=( (C-B)*x1+(A-C)*x2+(B-A)*x3 )/(2*G);
p.push_back( make_pair(X,Y) );
}
}
int main()
{
int n;
scanf("%d",&n);
point zero=point(0,0);
for( int i=1;i<=n;i++ ) scanf("%lf%lf",&a[i].x,&a[i].y);
for( int i=1;i<=n;i++ )
{
for( int j=i+1;j<=n;j++ ) solve(zero,a[i],a[j]);
}
sort(p.begin(),p.end());
int ans=0,cnt=0;
pair<db,db> now=p[0];
for( int i=0;i<p.size();i++ )
{
if(now==p[i] ) cnt++;
else now=p[i],cnt=1;
ans=max(ans,cnt);
}
for( int i=1;i<=n;i++ )
{
if( i*(i-1)==ans*2 )
{
printf("%d",i);
return 0;
}
}
}C.Cover the Tree
题意:给定一颗无根树,m条无向边,请找到最少的长链,覆盖所有无向边。
分析:长链的一端一定是叶子节点,我们先处理出每个点的度,然后找出所有度为1的叶子节点,然后随便指定一个点为根,进行深度遍历,记录每个结点的深度,然后将叶子节点根据深度从小到大排序,首尾选点进行组合成长链,如果叶子节点的个数是奇数个,那么排序后最中间的叶子结点连向根即可。(证明我也不会
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+7;
vector<int> g[maxn];
vector<int> L;
vector<int> R;
int root;
int fa[maxn];
void dfs( int x,int f )
{
int flag=1;
fa[x]=f;
int cnt=0;
for( int v : g[x] )
{
cnt++;
if( v==f ) continue;
flag=0;
dfs(v,x);
}
if( root==x && cnt==1 ) L.push_back(root);
if( flag ) L.push_back(x);
}
int d[maxn];
int main()
{
int n;
scanf("%d",&n);
for( int i=1;i<n;i++ )
{
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
d[u]++;
d[v]++;
if( d[i]>1 ) root=i;
}
dfs(root,root);
int m=L.size();
vector< pair<int,int> > ans;
// for( int i:L ) cout<<i<<" ";
for( int i=0;i<m/2;i++ )
{
ans.push_back( make_pair( L[i],L[m-1-i] ) );
}
if( m%2==1 )
{
ans.push_back( make_pair( L[m/2],root ) );
}
printf("%d\n",ans.size());
for( pair<int,int> v:ans )
{
printf("%d %d\n",v.first,v.second);
}
}
D.Duration
签到
F.Fake Maxpooling
题意:n * m的矩阵, .
定义k* k子矩阵的权值为矩阵内元素的最大值。求所有k* k矩阵的权值和。
分析: 先处理出 ,可以用记忆化gcd降至
.
对于k* k 的矩阵。
k=1 的时候每个ij位置的值就是权值。
k>1 的时候,可以简单验证得出i j 进行小等于k次减一操作后可以使得gcd(i,j)==1,获得最大的lcm(i,j).(打表可以验证次规律
那么转移方程: .
这种一种偷懒的计算方式。
当然也可以用单调队列解决k* k子矩阵的最大值。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5000+10;
int dp[maxn][maxn];
int a[maxn][maxn];
int gcd( int x,int y )
{
if( y==0 ) return x;
if( dp[x][y] ) return dp[x][y];
return dp[x][y]=gcd(y,x%y);
}
int main()
{
int n,m,k;
ll ans=0;
cin>>n>>m>>k;
for( int i=1;i<=n;i++ )
{
for( int j=1;j<=m;j++ )
{
if( k==1 ) a[i][j]=i*j/gcd(i,j);
else a[i][j]=max(i*j/gcd(i,j),max(a[i][j-1],a[i-1][j]));
if( i>=k && j>=k ) ans+=a[i][j];
}
}
printf("%lld\n",ans);
}G.Greater and Greater
题意:给定长度为n的a序列和长度为m的b序列,求在a序列中找到连续长度为m的子序列S,满足 .
求满足的序列个数。
分析:出题人说看到这种题目的数据范围就能想到 .(
反正我是写线段树和st表乱搞写自闭了
参考博客
我们先从简单的 分析。
表示从后面往前匹配,
串匹配到了第
位,
串匹配到了第
位,是否完全匹配。
计算答案只用每次加上 的值.
状态转移方程: .
内存为 一定MLE.
考虑用 优化第二维.
因为 的值只用两种 ,0或1.是否能用一个
表示第二维
的状态.
进一步分析,对于每个 与
进行比较,最后获得的状态一共有
种。
举个例子:对于样例B: 2 3 3 . 的状态就三种,000 ,100,111.
所以我们可以枚举出所有 可能状态.枚举有个小技巧先对
按照大小为第一关键字排序.
假如m=5,每个状态就是00000, 10000, 11000, 11100, 11110, 11111.每个状态比上一个状态多1.
每次让 ,并且让当前这位的初始位置上添加一个1----
.
可以在 的时间和空间内处理出来。
考虑 的
转移.
.
表示
对应的状态,下标
可以用二分求得。
因为 对应
的状态,我们只需要将
进行右移一位对应转移.
因为右移了一位, ,考虑
序列中第
位与
序列中的第
位匹配,根据
进行添1操作转移。
也可以用一个
变量进行滚动转移.
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
bitset<40010>a,b[maxn];
int n,m,ans,c[maxn],d[maxn],e[maxn];
int cmp( int x,int y ) { return d[x]<d[y]; }
int main()
{
scanf("%d%d",&n,&m);
for( int i=0;i<n;i++ ) scanf("%d",&c[i]);
for( int i=0;i<m;i++ ) scanf("%d",&d[i]),e[i]=i;
sort(e,e+m,cmp);
for( int i=1;i<=m;i++ ) b[i]=b[i-1],b[i].set(e[i-1]);
for( int i=n-1;i>=0;i-- )
{
a>>=1;
int l=0,r=m;
while( l<r )
{
int mid=l+r>>1;
if( c[i]<d[e[mid]] ) r=mid;
else l=mid+1;
}
a&=b[l];
if( c[i]>=d[m-1] ) a.set(m-1);
ans+=a[0];
}
printf("%d\n",ans);
}I. Interval
平面图转对偶图.网络流
H.Happy Triangle
题意:一个 序列。三种操作。
操作一: 删除一个元素 。
操作二: 添加一个元素 。
操作三: 询问序列中是否存在两个元素,使得和 作为三条边形成非退化三角形(面积不为0
分析:定义选择的两个元素 并且
,
一定是相邻数。
.
- 我们只需要在map中找到
的位置
,
之后的位置 两数相加都会 大于
.
- 再找区间
位置两数之差的最小值。 用线段树实现, 动态开点线段树, 每个值存于前面一个数的差值。 支持点修改,区间查询。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const int inf=1e9;
int n,m,op;
int ls[maxn*21],rs[maxn*21],val[maxn*21],cnt,rt;
map<int,int> mp;
void update( int &cur, int l,int r, int pos,int p )
{
if( !cur ) cur=++cnt,val[cnt]=p;
if( l==r )
{
val[cur]=p;return;
}
int mid=l+r>>1;
if( pos<=mid ) update(ls[cur],l,mid,pos,p);
if( pos>mid ) update(rs[cur],mid+1,r,pos,p);
int ans=inf*2;
if ( ls[cur] ) ans = min(ans, val[ls[cur]] );
if ( rs[cur] ) ans = min(ans, val[rs[cur]] );
val[cur] = ans;
}
int query_min( int cur,int l,int r,int L,int R )
{
if( l>r || !cur ) return inf*2;
if( L<=l && R>=r ) return val[cur];
int mid=(l+r)/2;
int ans=inf*2;
if( L<=mid ) ans=min(ans,query_min(ls[cur],l,mid,L,R));
if( R>mid ) ans=min(ans,query_min(rs[cur],mid+1,r,L,R));
return ans;
}
int query( int x )
{
int y=x/2+1;
auto it=mp.lower_bound(y);
if( it==mp.end() ) return inf*2;
if( it->second > 1 ) return it->first;
if( it!=mp.begin() )
{
auto l=it;
l--;
if( l->first+it->first > x ) return it->first;
}
if( (++it)!=mp.end() ) return it->first;
return inf*2;
}
void add( int x )
{
mp[x]++;
if( mp[x]==1 )
{
auto it=mp.lower_bound(x);
++it;
if( it!=mp.end() && it->second==1 ) // if it->second >1 差值为 0 是最优的 所以不需要更改
{
update(rt,0,inf,it->first,it->first-x );
}
it--;
int l=-inf*2;
if( it!=mp.begin() ) l=(--it)->first;
update(rt,0,inf,x,x-l);
}
else if( mp[x]==2 ) update(rt,0,inf,x,0);
}
void del( int x )
{
auto it=mp.lower_bound(x);
mp[x]--;
int l=-inf;
if( it!=mp.begin() ) l=(--it)->first,it++;
if( mp[x]==0 )
{
if( (++it)!=mp.end() && it->second==1 )
{
update(rt,0,inf,it->first,it->first-l);
}
update(rt,0,inf,x,inf*2);
mp.erase(x);
}
else if( mp[x]==1 ) update(rt,0,inf,x,x-l);
}
int main()
{
scanf("%d",&n);
for( int i=1;i<=n;i++ )
{
scanf("%d%d",&op,&m);
if( op==1 ) add(m);
if( op==2 ) del(m);
if( op==3 )
{
if( query_min(rt,0,inf,query(m),inf)<m ) puts("Yes");
else puts("No");
}
}
}J.Just Shuffle
题意:对于一个排列A,给定一个置换规则P,排列B{1,2,3,....,n}在使用置换P,K 次后得到排列A.输出第一次置换的结果.
分析:
因为B{1,2,3,..,n} 所以B置换一次输出的序列就是置换P。
,
为置换循环节的长度。
.
我们遍历A所有的置换环,枚举出,对每个
求出
.对第
个环的元素进行
次置换.就可以得到一个合法解。因为
是一个大质数,所以一定有解。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int vis[maxn],a[maxn],b[maxn];
vector<int> v;
int n,k;
void gao()
{
int r=v.size(),inv;
for( int i=0;i<r;i++ ) if( (ll)k*i%r==1 ) inv=i;
for( int i=0;i<r;i++ ) b[v[i]]=v[(i+inv)%r];
}
int main()
{
cin>>n>>k;
for( int i=1;i<=n;i++ ) cin>>a[i];
for( int i=1;i<=n;i++ )
{
if( !vis[i] )
{
v.clear();
int x=a[i];
while( !vis[x] )
{
vis[x]=1;
v.push_back(x);
x=a[x];
}
gao();
}
}
for( int i=1;i<=n;i++ ) printf("%d ",b[i]);
puts("");
}K.Keyboard Free
参考博客:https://www.cnblogs.com/wlzhouzhuan/p/13301358.html
非常棒的题解。
附一个自适应辛普森法求积分。
#include<bits/stdc++.h>
using namespace std;
typedef double db;
// https://www.luogu.com.cn/problem/P4525
db a,b,c,d,l,r;
const db PI=acos(-1.0);
inline db readb()
{
int f=1;db x=0;char s=getchar();
for( ;!isdigit(s);s=='-' && (f=-1),s=getchar());
for( ;isdigit(s);x=x*10+s-48,s=getchar());
if( s=='.' ) for( db l=0.1,s=getchar();isdigit(s);x=x+(s-48)*l,l/=10,s=getchar() );
return x*f;
}
// 积分
// ( 0 ~ π) H + r3 ×sin(x)
// ( 0 ~ alpha ) H - r3 ×sin(x)
// ( alpha ~ π- alpha ) r3 ×sin(x) - H
inline db f( db x ) // 在[l,r] 定积分收敛的函数
{
return pow(x,a/x-x); // 函数
}
inline db simpson( db l,db r ) //Simpson公式
{
db mid=(l+r)/2;
return ( f(l)+4*f(mid)+f(r) )*(r-l)/6;
}
db asr( db l, db r,db eps ,db ans )
{
db mid=(l+r)/2;
db ls=simpson(l,mid),rs=simpson(mid,r);
if( fabs(ls+rs-ans)<=15*eps ) return ls+rs+(ls+rs-ans)/15; //确认精度
return asr(l,mid,eps/2,ls)+asr(mid,r,eps/2,rs); //精度不够则递归调用
}
inline db asr( db l,db r,db eps ) { return asr(l,r,eps,simpson(l,r)) ; }
int main()
{
int t;
t=readb();
while( t-- )
{
db r1=readb(),r2=readb(),r3=readb();
if( r1>r2 ) swap(r1,r2);
if( r2>r3 ) swap(r2,r3);
if( r1>r3 ) swap(r1,r3);
db ans=0.0;
for( int i=1;i<=1000;i++ )
{
db sin_x=sin(2.0*PI/1000.0*i);
db cos_x=cos(2.0*PI/1000.0*i);
db X=r2*cos_x,Y=r2*sin_x;
db L=sqrt( (X-r1)*(X-r1)+Y*Y );
db H=Y/L*r1;
db alpha=asin(H/r3);
db h=(4.0*r3*cos(alpha)+4.0*alpha*H)/(2.0*PI);
ans+=h*L/2.0;
}
ans/=1000.0;
printf("%.1lf\n",ans);
}
}

京公网安备 11010502036488号