每一年都奔走在自己热爱里

没有人是一座孤岛,总有谁爱着你

AtCoder Beginner Contest 207(D,E)

AtCoder Beginner Contest 207(D,E)

D(计算几何)

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,同余)

E

这个题的大意是有\(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;
}
posted @ 2023-04-11 18:39  righting  阅读(24)  评论(0)    收藏  举报