ACM-ICPC 2017 Asia Urumqi

B. The Difference
水题,暴力跑一边permutation
G. The Mountain
水题,直接用叉积算一发面积

#include <bits/stdc++.h>
#include<cstring>
using namespace std;
#define ll long long
const int maxn = 201;
struct Point
{
    double x,y;
    Point(){}
    Point(double _x,double _y)
    {
        x = _x;
        y = _y;
    }
    double operator ^(const Point &b) const
    {
        return x*b.y - y * b.x;
    }
};
struct polygon
{
    int n;
    Point p[maxn];
    double getarea()
    {
        double sum = 0;
        for(int i = 0;i<n;i++)
        {

            sum += (p[i]^p[(i+1)%n]);

        }
        return fabs(sum)/2;
    }
};
int main()
{
    //freopen("input.txt","r",stdin);
    int T,cat = 1;
    cin>>T;
    while(T--)
    {
        polygon pl;
        cin>>pl.n;
        for(int i = 0;i<pl.n;i++)
        {
            cin>>pl.p[i].x>>pl.p[i].y;
        }
        cout<<setprecision(6)<<fixed<<pl.getarea()<<endl;
    }
    return 0;
}

D. Fence Building
把一个圆用k个点分成最多的区域是多少?
我们考虑二维欧拉公式1 = V-E+F 其中V代表点,E代表边,F代表面。
在院内,每四个点之间的形成一个面上的点,每两个点形成一条边,那么我们利用这个公式可以直接求出面的个数,也就是答案。
取模很是麻烦,稳妥起见。每次运算都要取模

#include <bits/stdc++.h>
#include<cstring>
using namespace std;
#define ll long long
#define mod 1000000007
long long pow_mod(long long a,long long n,long long m)//a^n mod m
{
    a%=mod;
    long long res=1ll;
    while(n>0)
    {
        if(n&1==1)
            res=res*a%m;
        a=a*a%m;
        n>>=1;
    }
    return res;
}
//记得在最后输出结果的时候再模m一次

ll quick_inverse(ll n, ll p) //传入a和mod-2,即quick_inverse(a,mod-2)
//返回a关于mod的逆元
{
    ll ret = 1,exponent = p;
    for (ll i = exponent; i; i >>= 1, n = n * n % mod)
        if (i & 1)
            ret = ret * n % mod;
    return ret;
}
int main()
{
    //freopen("input.txt","r",stdin);
    int T;
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++)
    {
        ll n;
        scanf("%lld",&n);

        ll ans=(((((((n%mod)*((n-1)%mod))%mod)*((n-2)%mod))%mod)*((n-3)%mod))%mod+((((((n-1)%mod)*((n-2)%mod))%mod)*12)%mod)%mod+(24*(n%mod))%mod)%mod;


        while(ans<0)ans+=mod;
        ans%=mod;
        ans*=quick_inverse(24ll,mod-2)%mod;
        ans%=mod;
        printf("Case #%d: %lld\n",cas,ans);
    }

    return 0;
}

K. Sum of the Line
题意是有点麻烦,实际上就是求一个后缀和。
然后我们把这个写成一个式子: a n s i = ∑ j n j 2 ∗ [ g c d ( i , j ) = 1 ] ans_i = \sum_{j}^{n}j^2*[gcd(i,j) = 1] ansi=jnj2[gcd(i,j)=1]
我们这样去理解,这个答案就是所有和n不互质的数的平方和,那么我们可以利用容斥原理:首先抛开gcd的影响,求出结果,对于每个质因子,我们减去或者加上这个质因子的贡献。
那么还要解决如何计算贡献的问题。我们知道 ∑ i n i 2 = n ( n + 1 ) ( 2 n + 1 ) 6 \sum_{i}^{n} i^2= {n(n+1)(2n+1)\over 6} ini2=6n(n+1)(2n+1),现在一开始的总和求出来了,枚举所有因子组合,我们要计算的平方和无论如何都能提出来这个因子的平方和,那么剩下的又变成了1^2 + 2^2 + 3^2…

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const ll mod = 998244353;

void getPrime()
{

}
ll pow_mod(ll a,ll n)
{
    a %=  mod;
    ll res = 1;
    while(n)
    {
        if(n & 1) res = res * a % mod;
        a = a* a % mod;
        n >>= 1;
    }
    return res;
}
vector<int> prime;
ll inv6;
ll func(ll n)
{
    return ((n * (n+1)% mod ) * (2*n % mod + 1)% mod) *inv6 % mod;
}
int main()
{
    inv6 = pow_mod(6,mod-2);
    int ca;
    cin>>ca;
    while(ca--)
    {
        ll n,k;
        cin>>n;
        k = n;

        prime.clear();
        for(ll i = 2;i*i<=n;i++)
        {
            if(k % i == 0)
            {
                prime.push_back(i);
                while(k % i == 0) k /= i;
            }
        }
        if(k != 1) prime.push_back(k);
        //for(int x : prime) cout<<x<<' '; cout<<endl;
        ll ans =0;
        int tot = prime.size();
        for(int i = 0;i<(1<<tot);i++)
        {
            int cnt = 0;ll den = 1LL;
            for(int j = 0;j<tot;j++)
            {
                if(i & (1<<j))
                {
                    den *= (ll)prime[j];
                    cnt++;
                }
            }
            if(cnt % 2 )
            {
                ans = (ans  - ((den * den % mod) * func(n/den) )% mod + mod) % mod;
            }
            else ans = (ans + (den * den % mod) * func(n/den)) % mod;
        }
        cout<<ans<<endl;
    }

    return 0;
}

I. A Possible Tree
一道带权并查集题放到了树上……
简要地说就是如果两个点之间的路径已经在一个关系里了,如果着一条路径不满足条件,说明已经到头了,如果能满足的话,就把这一次的结果叠加上去

#include <bits/stdc++.h>
#define ll long long
#define find fuck
#define next fuckme
using namespace std;
const int MAXN = 1e5 + 5;
int f[MAXN],sum[MAXN];
int find(int x)
{
    if(x == f[x]) return f[x];
    else
    {
        int fx = find(f[x]);
        sum[x] ^= sum[f[x]];
        return f[x] = fx;
    }
}
void init(int n)
{
    for(int i = 0;i<=n;i++)
    {
        f[i] = i;
        sum[i] = 0;
    }
}
int n,q;
int main()
{
    int ca;
    scanf("%d",&ca);
    while(ca--)
    {
        scanf("%d%d",&n,&q);
        init(n);
        for(int i = 0;i<n-1;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
        }
        int ans = q+1;
        for(int i = 1;i<=q;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            if(ans != q+1) continue;
            int fv =find(v);
            int fu = find(u);
            if(fv == fu && (sum[v] ^ sum[u]) != w)
            {
                ans = i;
            }
            else
            {
                f[fu] = fv;
                sum[fu] = sum[v] ^ sum[u] ^ w;
            }
        }
        printf("%d\n",ans-1);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值