AtCoder Beginner Contest 207(D,E)
D(计算几何)
这个题是两个点的集合,每个集合有\(n\)个点,我们可以让一个集合中的每一个点进行下面两种操作
\(1\),可以让每一个点进行旋转\(x\)的角度
\(2\),可以让每一个点的\(x\)变成\(x+disx\),\(y\)变成了\(y+disy\)
问是否可以让这两个集合重合(两个集合之间的点一一重叠)
对于这么两个集合,我们可以首项让这些点的重心都处于原点上
对于求重心\(corex\)和\(corey\),是每一个点的\(x\)和\(y\)的平均值
然后以\(a[1]\)这一点作为基准,判断要让\(b[j]\)这一个点和这一点可以重合,我们需要求出使重合需要旋转的角度
所以我们需要知道这两个点的角度
\(atan2(y,x)\)是求点\(x,y\)到原点这一线段和\(x\)轴之间的角度
所以我们要保证\(a[1]\)这一个不能是原点
然后对于要旋转的角度,就是\(atan2(a[1]_y,a[1]_x)-atan2(b[j]_y,a[j]_x)\)
我们要怎么求一个点在旋转\(angle\)之后的坐标呢
坐标为
\(tx=x\times sin(angle)-y\times cos(angle)\)
\(ty=y\times cos(angle)+y\times sin(angle)\)
然后对于其他的点,只要\(a\)点集合有一点可以和它重合即可(因为点集合里面的每一个点都是不一样的)
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include<cmath>
#include <bitset>
#include <unordered_map>
using namespace std;
const int maxn=200+10;
const int mod= 998244353;
const double eps=1e-8;
#define int long long
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
int n;
struct node
{
double x,y;
}a[maxn],b[maxn];
int sgn(double x)
{
if(fabs(x)<eps) return 0;
if(x<0) return -1;
return 1;
}
node rotate(node now,double angle)
{
node res;
double cosa=cos(angle),sina=sin(angle);
res.x=now.x*cosa-now.y*sina;
res.y=now.x*sina+now.y*cosa;
return res;
}
signed main ()
{
cin>>n;
double acorex=0,acorey=0;
double bcorex=0,bcorey=0;
for (int i=1;i<=n;i++)
{
cin>>a[i].x>>a[i].y;
acorex+=a[i].x;
acorey+=a[i].y;
}
acorex/=n;
acorey/=n;
for (int i=1;i<=n;i++)
{
cin>>b[i].x>>b[i].y;
bcorex+=b[i].x;
bcorey+=b[i].y;
}
bcorex/=n;
bcorey/=n;
for (int i=1;i<=n;i++)
{
a[i].x-=acorex;
a[i].y-=acorey;
b[i].x-=bcorex;
b[i].y-=bcorey;
}
for (int i=1;i<=n;i++)
{
if(sgn(a[i].x)&&sgn(a[i].y))
{
swap(a[i],a[1]);
break;
}
}
for (int i=1;i<=n;i++)
{
double angle=atan2(a[1].y,a[1].x)-atan2(b[i].y,b[i].x);
bool yes=true;
for (int j=1;j<=n;j++)
{
bool find=false;
node tmp=rotate(b[j],angle);
for (int k=1;k<=n;k++)
{
if(sgn(a[k].x-tmp.x)==0&&sgn(a[k].y-tmp.y)==0)
{
find=true;
break;
}
}
if(!find)
{
yes=false;
break;
}
}
if(yes)
{
cout<<"Yes\n";
system ("pause");
return 0;
}
}
cout<<"No\n";
system ("pause");
return 0;
}
E(dp,同余)
这个题的大意是有\(n\)个数,我们需要把这个数组分成\(k\)段,每一段的和为\(b_I\),并且\(b_i%i=0\),问有多少个划分方式可以划分完这\(n\)个数
我们可以预先求出前缀和
对于前一段已经存在了\(j-1\)个,此时的位置为\(i\)
只要\((sum[i]-sum[last])%j=0\),即可满足条件
对于\((sum[i]-sum[last])%j=0\),我们也可以知道这代表着\(sum[i]\)和\(sum[last]\)是同余的
所以我们把同余的组合都放在一起
故可得出状态转移方程为
dp[i][j];//分配了i个数,分成j份
s[i][j];//分成j份,%j=i的数量
s[0][1]=1;//初状态,一个,余1=0,,只有这一个状态
for (int i=1;i<=n;i++)
{
for (int j=1;j<=i;j++)
{
dp[i][j]=s[sum[i]%j][j];//把sum[x]%cnt看成一类,相同的余数(同余)
}
for (int j=1;j<=i+1;j++)
{
s[sum[i]%j][j]=s[sum[i]%j]+dp[i][j-1];//我们还需要更新同余的前缀
}
}
知道状态转移方程就很好办了(我感觉这个我很难想到)
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include<cmath>
#include <bitset>
#include <unordered_map>
using namespace std;
const int maxn=3000+10;
const double eps=1e-8;
const int mod=1e9+7;
#define int long long
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
int n;
int sum[maxn];
int s[maxn][maxn],dp[maxn][maxn];
signed main ()
{
cin>>n;
for (int i=1;i<=n;i++)
{
int x;
cin>>x;
sum[i]=sum[i-1]+x;
}
s[0][1]=1;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=i;j++)
{
dp[i][j]=s[sum[i]%j][j]%mod;
}
for (int j=1;j<=i+1;j++)
{
s[sum[i]%j][j]=(s[sum[i]%j][j]+dp[i][j-1])%mod;
}
}
int ans=0;
for (int i=1;i<=n;i++)
{
ans=(ans+dp[n][i])%mod;
}
cout<<ans<<"\n";
system ("pause");
return 0;
}
标签:AtCoder,const,207,Beginner,int,double,sum,maxn,include
From: https://www.cnblogs.com/righting/p/17307281.html