2018-2019 ACM-ICPC, Asia East Continent Finals

2018-2019 ACM-ICPC, Asia East Continent Finals

在此附上吉老师的视频题解
Problem C:Heretical … Möbius
思路:根据莫比乌斯系数的性质:4,9,25,49,121,169的倍数的系数均为0。因为题目给出了200个系数,那么假设 x x x为第1个数在系数表中的位置,所以我们可以得到以下方程
{ x ≡ a 1 , ( m o d   4 ) x ≡ a 2 , ( m o d   9 ) x ≡ a 3 , ( m o d   25 ) x ≡ a 4 , ( m o d   49 ) x ≡ a 5 , ( m o d   121 ) x ≡ a 6 , ( m o d   169 ) \begin{cases} x\equiv a_1,(mod 4)\\ x\equiv a_2,(mod 9)\\ x\equiv a_3,(mod 25)\\ x\equiv a_4,(mod 49)\\ x\equiv a_5,(mod 121)\\ x\equiv a_6,(mod 169) \end{cases} xa1,(mod 4)xa2,(mod 9)xa3,(mod 25)xa4,(mod 49)xa5,(mod 121)xa6,(mod 169)
接下来就是找出所有的 a i a_i ai,然后暴力枚举 a i a_i ai,利用中国剩余定理来计算出 x x x并验证。

#include<bits/stdc++.h>
using namespace std;
const int MAX=5e4+10;
const int INF=1e9+7;
const int LCM=901800900;
typedef long long ll;
vector<ll>v[6];
vector<ll>pr;
ll u[MAX];
ll g[6]={4,9,25,49,121,169};
ll num[210];
ll p[6];
ll ans=INF;
ll mu(ll x)
{
    if(x<MAX)return u[x];
    for(int i=0;i<pr.size();i++)
    {
        if(pr[i]*pr[i]>x)break;
        if(x%(pr[i]*pr[i])==0)return 0;
        while(x%pr[i]==0)x/=pr[i];
    }
    return 1;
}
void exgcd(ll a,ll b,ll &gcd,ll &x,ll &y)
{
    if(b==0){x=1,y=0;gcd=a;}
    else{exgcd(b,a%b,gcd,y,x);y-=(a/b)*x;}
}
ll CRT(int n,ll *a,ll *m)
{
    ll M=m[0],ANS=a[0],gcd,x,y;
    for(int i=1;i<n;i++)
    {
        ll c=(a[i]-ANS%m[i]+m[i])%m[i];
        exgcd(M,m[i],gcd,x,y);
        ll g=m[i]/gcd;
        if(c%gcd!=0)return -1;
        x=(x%g)*((c/gcd)%g);
        x=(x%g+g)%g;
        ANS=M*x+ANS;
        M=M*g;
        ANS=(ANS%M+M)%M;
    }
    return (ANS%M+M)%M;
}
void cal()//利用CRT计算x并验证
{
    for(ll i=CRT(6,p,g);i<1e9;i+=LCM)
    {
        if(i+199>1e9)break;
        if(i>=ans)break;
        int ok=1;
        for(ll j=i;j<=i+199;j++)if(mu(j)!=num[j-i+1]){ok=0;break;}
        if(ok){ans=min(ans,i);break;}
    }
}
void dfs(int k)//暴力枚举ai
{
    if(k==6){cal();return;}
    for(int j=v[k].size()-1;j>=0;j--)//如果j改为从0开始连过样例都很艰难
    {
        p[k]=v[k][j];
        dfs(k+1);
    }
}
bool isp[MAX];
void init()
{
	memset(isp,0,sizeof isp);
    for(int i=1;i<MAX;i++)u[i]=1;
	for(int i=2;i<MAX;i++)
	{
		if(!isp[i])
        {
            pr.push_back(i);
            for(ll j=1ll*i*i;j<MAX;j+=1ll*i*i)u[j]=0;
        }
		for(int j=0;j<pr.size();j++)
		{
			long long t = 1LL*pr[j]*i ;
			if(t<MAX)
			{
				isp[t] = 1;
				if(i%pr[j]==0)break;
			}
			else break;
		}
	}
}
int main()
{
    init();
    int tot=0;
    for(int i=0;i<10;i++)
    {
        char s[30];
        scanf("%s",s);
        for(int j=1;j<=20;j++)num[i*20+j]=s[j-1]-'0';
        for(int j=0;j<20;j++)tot+=s[j]-'0';
    }
    if(tot<=100)goto QWQ;    //连续的200个数里,至少有一半以上的数没有平方因子
    for(int i=0;i<6;i++)
    for(int j=1;j<=g[i];j++) //计算出ai存入v
    {
        int ok=1;
        for(int k=j;k<=200&&ok;k+=g[i])ok=(num[k]^1);
        if(ok)v[i].push_back((g[i]-j+1)%g[i]);
    }
    for(int i=0;i<6;i++)if(v[i].size()==0)goto QWQ;
    dfs(0);
    if(ans!=INF){cout<<ans<<endl;return 0;}
    QWQ:puts("-1");
    return 0;
}

Problem F:Interstellar … Fantasy
思路:将S,T,O投影到一个二维平面上计算,还要判断ST是否与圆O相交。

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
struct Point{int x,y,z;}O,S,T;
Point operator-(Point A,Point B){return (Point){A.x-B.x,A.y-B.y,A.z-B.z};}
int operator*(Point A,Point B){return A.x*B.x+A.y*B.y+A.z*B.z;}
int operator==(Point A,Point B){return A.x==B.x&&A.y==B.y&&A.z==B.z;}
int dis2(Point A,Point B){return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)+(A.z-B.z)*(A.z-B.z);}
double dis(Point A,Point B){return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)+(A.z-B.z)*(A.z-B.z));}
int main()
{
    int cas;
    cin>>cas;
    while(cas--)
    {
        int R;
        scanf("%d%d%d%d",&O.x,&O.y,&O.z,&R);
        scanf("%d%d%d",&S.x,&S.y,&S.z);
        scanf("%d%d%d",&T.x,&T.y,&T.z);
        if(S==T){puts("0");continue;}//S T重合
        double OST=acos((dis2(S,O)+dis2(S,T)-dis2(O,T))/(2*dis(O,S)*dis(S,T)));
        double h=sin(OST)*dis(S,O);     //圆心到ST的距离
        if(h>=R||(O-S)*(T-S)<0||(O-T)*(S-T)<0){printf("%.10f\n",dis(S,T));continue;}//ST不与圆相交
        double ang=acos((dis2(S,O)+dis2(T,O)-dis2(S,T))/(2*dis(S,O)*dis(T,O)));
        double ans=sqrt(dis2(O,S)-R*R)+sqrt(dis2(O,T)-R*R);
        ans+=(ang-acos(R/dis(O,S))-acos(R/dis(O,T)))*R;
        printf("%.10f\n",ans);
    }
    return 0;
}

Problem I: Misunderstood … Missing
思路:倒着DP。 d [ i ] [ j ] [ k ] d[i][j][k] d[i][j][k]表示当前为第 i i i个回合,攻击了 j j j次,攻击回合的下标和为 k k k的最大伤害值。

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
ll a[110],b[110],c[110];
ll d[2][101][5051];
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
        memset(d,-1,sizeof d);
        int pre=0,now=1;
        for(int i=n;i>=1;i--)
        {
            if(i==n)d[now][1][n]=a[i];
            else
            {
                for(int j=1;j<=n-i;j++)
                for(int k=n;k<=(i+n)*(n-i+1)/2;k++)
                {
                    if(d[pre][j][k]==-1)continue;
                    d[now][j+1][k+i]=max(d[now][j+1][k+i],d[pre][j][k]+a[i]);
                    d[now][j][k]=max(d[now][j][k],d[pre][j][k]+b[i]*(k-j*i));
                    d[now][j][k]=max(d[now][j][k],d[pre][j][k]+c[i]*j);
                }
            }
            now^=1;
            pre^=1;
            memset(d[now],-1,sizeof d[now]);
        }
        ll ans=0;
        for(int i=1;i<=n;i++)
        for(int j=n;j<=(1+n)*n/2;j++)ans=max(ans,d[pre][i][j]);
        cout<<ans<<endl;
    }
    return 0;
}

Problem L: Eventual … Journey
思路:我们可以将节点分为4种:与1无边相连的0点,这种点记为 A A A;与1相连的0点,记为 B B B;与0相连的1点,记为 C C C;与0不相连的1点,记为 D D D

A与B之间的距离为1;
A与C之间的距离为2(当存在0点与1点相连的边时);
A与D之间的距离为3(当存在0点与1点相连的边时)。

任意两节点距离最大为3,我们记录一下每个点与互异的点的连边数,最后遍历一边讨论一下。

#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
const int MOD=1e9+7;
typedef long long ll;
int cnt[MAX];
int ans[MAX];
int a[MAX];
int main()
{
    int n,m,zero=0,one=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(a[i]==0)zero++;
        else one++;
    }
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(a[x]!=a[y])
        {
            cnt[x]++;
            cnt[y]++;
        }
    }
    int A=0,B=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]==0&&cnt[i])A++;
        if(a[i]==1&&cnt[i])B++;
    }
    for(int i=1;i<=n;i++)
    {
        ans[i]+=cnt[i];
        if(a[i]==0)ans[i]+=zero-1;
        else ans[i]+=one-1;
        if(cnt[i]==0)
        {
            if(a[i]==0)
            {
                ans[i]+=2*B;
                ans[i]+=3*(one-B);
            }
            else
            {
                ans[i]+=2*A;
                ans[i]+=3*(zero-A);
            }
        }
        else
        {
            if(a[i]==0)ans[i]+=2*(one-cnt[i]);
            else ans[i]+=2*(zero-cnt[i]);
        }
    }
    for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' ');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值