2015年ACM上海大都会邀请赛总结

更新记录

H题成功拿到HDU statistic rank1!(我TM是有多闲。。)
已经完成赛后AK!可喜可贺!虽然这个AK的周期略长。。都是我太弱。。
2015年ACM上海大都会邀请赛总结_第1张图片

比赛经历

非常神奇的一场比赛,两百多个队在一个体育馆(羽毛球馆)内比赛,两桌距离不超过半米。自己的桌牌在别人的电脑后面,直接导致我们对面的队一开始直接坐了我们的位置。正式比赛还拿了我们的题。。。


热身赛

首先,题目直接在比赛前放在了桌上。没有信封!
tomriddly直接抄起了题看了一眼,发现是题,就扣下了!但是这一瞬间就读完了A题题意!然后!然后!在调试Eclipse的时候!写完了A题。
结果。
结果。
并没有拿到runid 1
交上去已经runid 14了
然后,
就没有然后了。

A题 求2的n次方,要用高精度。
B题 给一个n*m的考场,有一些位置不能坐人,每个人能看到左右以及左前方右前方的人的试卷。(类似于国际象棋棋盘上放最多的兵),二分图即可。
C题 有n道题,给出每道题通过的人数,问过了C道题及以上的人最多有多少。
这题我们当时没有想到正解。其实要想每个人都过题多,只要尽量使得每人过题数尽量平均即可。这时候可以想到二分总人数,来验证答案。再更新一个最大值即可。


正赛

jiefangxuanyan一开始就瞄上了J题,然后以迅雷不及掩耳之势算出了公式,成功1Y拿下一血和一个很胖的一血气球(志愿者发气球的时候还砸到了对面的同学的脑袋,简直了)。然后看到榜上E过了好多,于是tomriddly就写了E,然后1Y。然后jiefangxuanyan刷刷刷敲了B题,是个模拟。然后我们觉得D应该是一个线段树,在解决了longlong*longlong的问题之后(汇编乘法挂,详情请问本人),tomriddly一顿敲,直到觉得自己写不出像样的lazy标记。。
这时znl1087jiefangxuanyan发现F是个脑筋急转弯。。。。
然后把tomriddly赶下键盘,jiefangxuanyan成功使用java获得1Y
然后是一个漫长的空键盘阶段,将近一个小时。。当时场上一部分人过了G题,一部分人过了A,有几个人过了D。然后开始每个人轮流想这几道题。。在一个小时之后,tomriddly想出了G的做法。虽然znl1087jiefangxuanyan都没听明白,但是本着不让键盘空着(虽然TMD已经空了一个小时)的原则,让他上去写了。好久之后(其实就是他写了好久好久,调了好久好久,期间还上了一个大号),写完了,但是WA了。这个时候jiefangxuanyan成功推出A题公式。。然后上键盘微调1Y。然后tomriddly惊奇的发现自己的程序居然写的是int dfs(xxxxx) 说好的long long呢!
然后过了。此时大概还有一个半小时。然后tomriddly说我们一定还要再过一题!我们俩很认同的点了点头。然后三个人同时想D。
然后jiefangxuanyan感觉这个巨大的MOD有一些奇怪,应该有一些奇怪的性质。。znl1087提出,可能会出现循环。然后用python打了个表。居然真TM有循环,并且经过测试,30次以内必出!
于是,去掉lazy,特别简单的线段树,1Y(当然是开了某个挂的)
然后已经A了7题,还剩48分钟。估计了一下排名,大概金牌是稳了。。于是tomriddly劝告我们,再做一题就赚了!不然也不亏(废话)
于是大家一起找I题的规律。。。。
然而并没有找出来。。。。
然后比赛就结束了。。。。。


午饭

赛前我们三个人讨论了一下当天的午饭会发啥,tomriddly预测应该会比省赛四省赛的时候工大发的好。znl1087根据赛场布置风格推测,估计连马可波罗肠都没有。。。结果。。。结果。。。
奶茶加面包。。。。
没了。。
没了。。


正赛题解


A Article

题意

DRD经常使用一个文本处理软件,这个软件每输入一个字符就有一定的概率(p)崩溃,并且丢失上次保存之后的所有数据。执行一次保存需要x字符的代价(但是不会崩溃)问在最优策略下,输入字符的期望是多少

思路

首先考虑一次性完成i个字符输入的期望e[i]。发现是一个近似的等比数列。根据推出来的公式,每次保存之间的间隔尽量平均是最优解。预处理出e[i],然后枚举保存的次数,O(1)计算出期望,取期望最小的值为答案。

证明

如图:
2015年ACM上海大都会邀请赛总结_第2张图片

AC代码

HDU5236,注意这题没有special judge,不写%.6f输出会WA

/* written by jiefangxuanyan */
#include 
#include 
#include 
#include 
const int N=110000;
double t[N];
int main(){
    int cn;
    scanf("%d",&cn);
    for(int ci=1;ci<=cn;ci++){
        int n;
        double fail;
        int save;
        scanf("%d%lf%d",&n,&fail,&save);
        t[0]=0;
        for(int i=1;i<=n;i++){
            t[i]=(t[i-1]+1)/(1-fail);
        }
        t[n+1]=0;
        double s=DBL_MAX;
        for(int k=1;k<=n;k++){
            int left=n%k;
            s=std::min(t[n/k+1]*left+t[n/k]*(k-left)+save*k,s);
        }
        printf("Case #%d: %.6f\n",ci,s);
    }
    return 0;
}

B Base64

题意

base64编码,对一个字符串进行n次base64编码,输出答案。

思路

同上。。

AC代码

hdu5237

/* written by tomriddly */
#include 
using namespace std;
string Encode(const char* Data,int DataByte)
{
    //编码表
    const char EncodeTable[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    //返回值
    string strEncode;
    char Tmp[4]= {0};
    int LineLength=0;
    for(int i=0; i<(int)(DataByte / 3); i++)
    {
        Tmp[1] = *Data++;
        Tmp[2] = *Data++;
        Tmp[3] = *Data++;
        strEncode+= EncodeTable[Tmp[1] >> 2];
        strEncode+= EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F];
        strEncode+= EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F];
        strEncode+= EncodeTable[Tmp[3] & 0x3F];
    }
    //对剩余数据进行编码
    int Mod=DataByte % 3;
    if(Mod==1)
    {
        Tmp[1] = *Data++;
        strEncode+= EncodeTable[(Tmp[1] & 0xFC) >> 2];
        strEncode+= EncodeTable[((Tmp[1] & 0x03) << 4)];
        strEncode+= "==";
    }
    else if(Mod==2)
    {
        Tmp[1] = *Data++;
        Tmp[2] = *Data++;
        strEncode+= EncodeTable[(Tmp[1] & 0xFC) >> 2];
        strEncode+= EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)];
        strEncode+= EncodeTable[((Tmp[2] & 0x0F) << 2)];
        strEncode+= "=";
    }

    return strEncode;
}
int n;
string a;
int main()
{
    int fuck;
    scanf("%d", &fuck);
    for (int cas = 1; cas <= fuck; cas++)
    {
        cin >> n >> a;
        for (int i = 1; i <= n; i++)
            a = Encode(a.c_str(), a.length());
        cout << "Case #" << cas << ": " << a << endl;
    }
    return 0;
}

C Calculator

题意

给出一个计算的序列,包含{+,*,^}三种运算。给出两种操作,1是给出初始值,求按照序列计算的答案,2是修改序列中某个位置的数和运算符。

思路

比赛的时候没想出来,比完赛想出来了。首先题目中,用来MOD的值是一个比较小的数,所以大概思路是把一个区间的运算维护成一个a[mod]大小的映射。但是mod = 29393貌似又太大,于是想到把29393 = 7 * 13 * 17 * 19,然后分别维护这四个因数的线段树,然后利用中国剩余定理算出最后答案。维护区间用线段树去实现,可以把映射单独写成一个数据结构,然后开这个数据结构的线段树。
单点修改,并且只查询根节点,线段树操作非常简单。
jiefangxuanyan写代码的时候碰到一些细节问题,不过比赛的时候队友在旁边看,肯定不会出现这些问题的。

AC代码

HDU5238

/* written by jiefangxuanyan */
#include 
#include 
#include 
template<int base>
class Mapping{
    int tgt[base];
public:
    int &operator[](int pos){
        return tgt[pos];
    }
    void clear(){
        for(int i=0;iint a){
        for(int i=0;i%base;
        }
    }
    void setMul(int a){
        for(int i=0;i*a)%base;
        }
    }
    void setPow(int a){
        for(int i=0;i1;
            int cur=i;
            for(int p=a;p;p>>=1){
                if(p&1){
                    tgt[i]=(tgt[i]*cur)%base;
                }
                cur=(cur*cur)%base;
            }
        }
    }
    void combine(Mapping &a,Mapping &b){
        for(int i=0;iint N=55000;
int mask;
template<int base>
class SegTree{
    static const int SZ=4*N;
    Mapping data[SZ];
    static int left(int a){
        return a<<1;
    }
    static int right(int a){
        return left(a)|1;
    }
    void update(int a){
        for(a>>=1;a;a>>=1){
            data[a].combine(data[left(a)],data[right(a)]);
        }
    }
public:
    void clear(){
        for(int i=0;iint pos,int a){
        pos|=mask;
        data[pos].setAdd(a);
        update(pos);
    }
    void setMul(int pos,int a){
        pos|=mask;
        data[pos].setMul(a);
        update(pos);
    }
    void setPow(int pos,int a){
        pos|=mask;
        data[pos].setPow(a);
        update(pos);
    }
    int get(int a){
        a%=base;
        return data[1][a];
    }
};
SegTree<7> _7;
SegTree<13> _13;
SegTree<17> _17;
SegTree<19> _19;
int inv(int a,int m){
    return a==1?1:inv(m%a,m)*(m-m/a)%m;
}
int main(){
    const int M=7*13*17*19;
    const int MA=M/7,MB=M/13,MC=M/17,MD=M/19;
    const int A=MA*inv(MA%7,7),
        B=MB*inv(MB%13,13),
        C=MC*inv(MC%17,17),
        D=MD*inv(MD%19,19);
    int cn;
    scanf("%d",&cn);
    for(int ci=1;ci<=cn;ci++){
        printf("Case #%d:\n",ci);
        _7.clear();
        _13.clear();
        _17.clear();
        _19.clear();
        int n,m;
        scanf("%d%d",&n,&m);
        mask=1;
        while(mask<=n){
            mask<<=1;
        }
        for(int i=0;iint a;
            scanf(" %c%d",&op,&a);
            if(op=='+'){
                _7.setAdd(i,a);
                _13.setAdd(i,a);
                _17.setAdd(i,a);
                _19.setAdd(i,a);
            } else if(op=='*'){
                _7.setMul(i,a);
                _13.setMul(i,a);
                _17.setMul(i,a);
                _19.setMul(i,a);
            } else if(op=='^'){
                _7.setPow(i,a);
                _13.setPow(i,a);
                _17.setPow(i,a);
                _19.setPow(i,a);
            }
        }
        for(int i=0;i<m;i++){
            int type;
            scanf("%d",&type);
            if(type==1){
                int a;
                scanf("%d",&a);
                printf("%d\n",(A*_7.get(a)+B*_13.get(a)+C*_17.get(a)+D*_19.get(a))%M);
            } else if(type==2){
                int pos;
                char op;
                int a;
                scanf("%d %c%d",&pos,&op,&a);
                pos--;
                if(op=='+'){
                    _7.setAdd(pos,a);
                    _13.setAdd(pos,a);
                    _17.setAdd(pos,a);
                    _19.setAdd(pos,a);
                } else if(op=='*'){
                    _7.setMul(pos,a);
                    _13.setMul(pos,a);
                    _17.setMul(pos,a);
                    _19.setMul(pos,a);
                } else if(op=='^'){
                    _7.setPow(pos,a);
                    _13.setPow(pos,a);
                    _17.setPow(pos,a);
                    _19.setPow(pos,a);
                }
            }
        }
    }
    return 0;
}

D Doom

题意

给出n个数和一个初始值为0的答案。每次操作给出一个区间[l,r],把区间所有的数加到答案中,之后把区间的每个数都平方。每次操作都需要输出答案 mod 9223372034707292160(2 ^ 63 - 2 ^ 31)

思路

首先,想到这个题可以用线段树来维护区间的和,然后对于(2 ^ 63 - 2 ^ 31)这个数,我们发现了一些神奇的性质,即任意数的平方MOD此数,重复操作至多29次就会进入一个不变的数。因此,可以在线段树维护时,直接限定次数。具体做法是维护每个区间中更新次数最少的数量,如果发现更新次数等于30就不往下更新,否则继续往下更新。

AC代码

HDU 5239

/*************************************************************************
    > File Name: pd.cpp
    > Author: tomriddly 
    > Created Time: 三  5/27 05:45:39 2015
 ************************************************************************/

#include 
using namespace std;
typedef unsigned long long ll;
const ll MOD = 9223372034707292160LL;
const int MAXN = 111111;
int n, m, cnt[MAXN << 2];
ll tree[MAXN << 2], a[MAXN];
inline ll mulmod(ll x, ll y, ll mod)
{
    ll ret = 0;
    __asm__("movq %1,%%rax\n imulq %2\n idivq %3\n":"=d"(ret):"m"(x),"m"(y),"m"(mod):"%rax");
    return ret;
}
#define lx (x << 1)
#define rx (lx | 1)
#define mid ((l + r) >> 1)
inline void pushup(const int &x)
{
    tree[x] = (tree[lx] + tree[rx]) % MOD;
    cnt[x] = min(cnt[lx], cnt[rx]);
}

inline void build(const int &x, const int &l, const int &r)
{
    if (l == r)
    {
        tree[x] = a[l];
        cnt[x] = 0;
        return ;
    }
    build(lx, l, mid);
    build(rx, mid + 1, r);
    pushup(x);
}

inline ll query(const int &x, const int &l, const int &r, const int &ql, const int &qr)
{
    if (l == ql && r == qr)
        return tree[x];
    if (mid >= qr)
        return query(lx, l, mid, ql, qr);
    if (mid < ql)
        return query(rx, mid + 1, r, ql, qr);
    return (query(lx, l, mid, ql, mid) + query(rx, mid + 1, r, mid + 1, qr)) % MOD;
}

inline void modify(const int &x, const int &l, const int &r, const int &ql, const int &qr)
{
    if (cnt[x] == 30)
        return ;
    if (l == ql && r == qr && l == r)
    {
        tree[x] = mulmod(tree[x], tree[x], MOD), cnt[x]++;
        return ;
    }
    if (mid >= qr)
        modify(lx, l, mid, ql, qr);
    else if(mid < ql)
        modify(rx, mid + 1, r, ql, qr);
    else
    {
        modify(lx, l, mid, ql, mid);
        modify(rx, mid + 1, r, mid + 1, qr);
    }
    pushup(x);
}

int main()
{
    int fuck;
    scanf("%d", &fuck);
    for (int cas = 1; cas <= fuck; cas++)
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
            scanf("%I64d", a + i);
        build(1, 1, n);
        printf("Case #%d:\n", cas);
        ll ans = 0;
        while (m--)
        {
            int l, r;
            scanf("%d%d", &l, &r);
            ans = (ans + query(1, 1, n, l, r)) % MOD;
            modify(1, 1, n, l, r);
            printf("%I64d\n", ans);
        }
    }
    return 0;
}

E Exam

题意

有n门考试,每场考试有需要复习的时间,开始时间及持续时间。可以在非连续的时间复习,考试时不能复习。问能否通过全部的考试。

思路

从前往后直接模拟,按照考试顺序复习,判断能否完成即可。

AC代码

HDU5240

/* written by tomriddly */
#include 
using namespace std;
const int MAXN = 111111;
struct Fuck
{
    long long r, s, t;
    inline void get()
    {
        scanf("%I64d%I64d%I64d", &r, &s, &t);
        t += s;
    }
    inline friend bool operator <(const Fuck &x, const Fuck &y)
    {
        return x.s < y.s;
    }
}a[MAXN];
int n;
int main()
{
    int fuck;
    scanf("%d", &fuck);
    for (int cas = 1; cas <= fuck; cas++)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            a[i].get();
        sort(a + 1, a + 1 + n);
        int i = 1, j = 1;
        long long rest = a[1].s;
        bool haveans = true;
        while (i <= n)
        {
            if (rest < a[i].r)
            {
                if (i == j)
                {
                    haveans = false;
                    break;
                }
                a[i].r -= rest;
                j++;
                rest = a[j].s - a[j - 1].t;
            }
            else
            {
                rest -= a[i].r;
                i++;
            }
        }
        printf("Case #%d: ", cas);
        if (!haveans)
            puts("NO");
        else
            puts("YES");
    }
    return 0;
}

F Friends

题意

Mike和他的朋友们喜欢学习语言。然后Mike是最DIAO的,他会n门语言,而他的朋友们会的都是他的子集。且彼此之间有如下关系:
。。。。。。。。。。。谁是谁的超集之类的。。。。。。
好多关系
不想写了。。。。。。虽然并没有什么卵用。。。
求满足这些条件的集合种数

思路

以为是拓扑排序,树形DP。。。
结果后来发现每种语言之间是独立的。。
所以答案肯定是k^n。。。
通过sample input and sample output发现。。
k = 32。。java过之。。

AC代码

HDU5241

/* written by tomriddly */
import java.util.*;
import java.math.*;

public class Main
{
    static public void main(String[]args)
    {
        Scanner cin = new Scanner(System.in);
        int fuck = cin.nextInt();
        for (int cas = 1; cas <= fuck; cas++)
        {
            int n = cin.nextInt();
            System.out.printf("Case #%d: ", cas);
            System.out.println(BigInteger.valueOf(32).pow(n));
        }
        cin.close();
    }
}

G Game

题意

一棵树有n个节点,每个节点有一个价值。一个人从根节点走到某叶子节点算一次游戏,可以获得经过节点的所有价值。但每个节点的价值只能被获得一次。问在同一棵树上进行K次游戏,最多能获得多少价值。

思路

对于每个节点,统计从这个点走到根节点能获得的价值之和。一次DFS搞定。把这些节点按照这个和从大到小排序。按照这个顺序,再次统计到根节点的价值之和,再次DFS。这次把已经走过的节点价值设为0。按照这次统计的和进行排序。然后从前往后取K次,每次用dfs把叶子节点到根节点的一条链拿干净,不重复拿取。统计价值之和,即为答案。
证明略。。

AC代码

HDU5242

/*************************************************************************
    > File Name: pg.cpp
    > Author: tomriddly
    > Created Time: 三  5/27 05:15:42 2015
 ************************************************************************/

#include 
using namespace std;
const int MAXN = 111111;
long long a[MAXN];
int n, m, pos[MAXN];
bool vis[MAXN];
struct Arc
{
    int dest;
    Arc *next;
    inline Arc() {}
    inline Arc(const int &dest, Arc *next):dest(dest), next(next) {}
}Npool[MAXN], *Nptr, *adj[MAXN];

struct Fuck
{
    int i;
    long long sum;
    inline friend bool operator <(const Fuck &x, const Fuck &y)
    {
        return x.sum > y.sum;
    }
}s[MAXN];

inline void insert(const int &start, const int &finish)
{
    adj[start] = new(Nptr++)Arc(finish, adj[start]);
}

inline long long dfs(const int &x)
{
    if (s[x].sum)
        return s[x].sum;
    s[x].sum = a[x];
    for (Arc *p = adj[x]; p; p = p->next)
        s[x].sum += dfs(p->dest);
    return s[x].sum;
}

inline long long dfs2(const int &x)
{
    if (s[pos[x]].sum)
        return 0;
    s[pos[x]].sum = a[x];
    for (Arc *p = adj[x]; p; p = p->next)
        s[pos[x]].sum += dfs2(p->dest);
    return s[pos[x]].sum;
}

inline void dfs3(const int &x)
{
    if (vis[pos[x]])
        return ;
    vis[pos[x]] = true;
    for (Arc *p = adj[x]; p; p = p->next)
        dfs3(p->dest);
}

int main()
{
    int fuck;
    scanf("%d", &fuck);
    for (int cas = 1; cas <= fuck; cas++)
    {
        Nptr = Npool;
        memset(adj, 0, sizeof(adj));
        memset(s, 0, sizeof(s));
        memset(vis, false, sizeof(vis));
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
        {
            scanf("%I64d", a + i);
            s[i].i = i;
        }
        for (int i = 1; i < n; i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            insert(v, u);//因为是从叶子节点向上计算,所以反向建边
        }
        for (int i = 1; i <= n; i++)
            if (!s[i].sum)
                dfs(i);
        sort(s + 1, s + 1 + n);
        for (int i = 1; i <= n; i++)
            s[i].sum = 0, pos[s[i].i] = i;
        for (int i = 1; i <= n; i++)
            if (!s[i].sum)
                dfs2(s[i].i);
        sort(s + 1, s + 1 + n);
        for (int i = 1; i <= n; i++)
            pos[s[i].i] = i;
        long long ans = 0;
        int cnt = 0;
        for (int i = 1; i <= n && cnt < m; i++)
            if (!vis[i])
            {
                dfs3(s[i].i);
                ans += s[i].sum, cnt++;
            }
        printf("Case #%d: %I64d\n", cas, ans);
    }
    return 0;
}

H Homework

题意

题意比较复杂。。不想说。。直接讲做题过程。。
这道题是一道没那么难的计算几何。。。裸半平面交。。
当时比赛没看完全是跟着榜了。。要是比赛的时候看了就能过了。。
作为只有AK队过的题,一直以为很难。其实如果不限内存的话挺好过的。
HDU上面这道题的内存限制是丧病的32M,如果开100W的double,开几个就直接爆了。。
大概是我比较2。。。没想到怎么优化内存,一直MLE
要是比赛的时候写了就好了,,不限内存。。。
2015年ACM上海大都会邀请赛总结_第3张图片
最后过的方法是,把Line结构体里的Point用short记录输入的点的索引,从而节省好几个double的空间。。
然后就过了。。时间HDUrank1!(其实关云长是我在VJ上交的。。)
2015年ACM上海大都会邀请赛总结_第4张图片

思路

对于给定的所有点,求出任意两个点之间的直线两侧的点的个数。如果某条直线单侧的点的个数小于[n/3],那么就把这条直线放入一个vector中存下来。全部扫一遍,把这些直线做一遍半平面交,算一下面积就行了。注意扫的方法,枚举每个点作为中心点,跟其他点连线,然后按照极角排序。用队列维护,扫一圈就可以把值统计出来了。

AC代码

HDU5243

/*************************************************************************
  > File Name: ph.cpp
  > Author: znl1087
  > Mail: [email protected] 
  > Created Time: 一  6/ 1 21:22:30 2015
 ************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
int n;
int tcnt;
struct Point{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
}in[1005];
typedef Point Vec;
//向量+向量 = 向量,点+向量 = 点
Vec operator +(Vec A,Vec B){return Vec(A.x+B.x,A.y+B.y);}
//点-点 = 向量
Vec operator -(Point A,Point B){return Vec(A.x-B.x,A.y-B.y);}
//向量*数 = 向量
Vec operator *(Vec A,double p){return Vec(A.x*p,A.y*p);}
//向量/数 = 向量
Vec operator /(Vec& A,double p){return Vec(A.x/p,A.y/p);}
bool operator <(const Point& a,const Point& b){
    return a.xconst double EPS = 1e-7;
int dcmp(double x){
    if(fabs(x)return 0;else return x<0? -1: 1;
}
bool operator == (const Point& a,const Point &b){
    return dcmp(a.x-b.x)==0 &&dcmp(a.y-b.y) == 0;
}
double ang(Vec v){return atan2(v.y,v.x);}
/*==========以上为基本定义============*/
double Dot(Vec& A,Vec& B){return A.x*B.x+A.y*B.y;}
double Length(Vec& A){ return sqrt(Dot(A,A));}
double Angle(Vec& A,Vec& B){return acos(Dot(A,B)/Length(A)/Length(B));}
/*==========用点积算向量长度和两个向量夹角============*/
double Cross(Vec A,Vec B){return A.x*B.y - A.y*B.x;}
double PolygonArea(vector& p){
    int n = p.size();
    double area = 0;
    for(int i=1;i1;i++)
        area += Cross(p[i] - p[0],p[i+1]-p[0]);
    return fabs(area/2);
}
struct Line{
    short P1;
    short P2;
    double ang;
    short left;
    Line(){}
    Line(short p1,short p2){
        P1 = p1,P2 = p2;
        Vec v = in[p2]-in[p1];
        ang  = atan2(v.y,v.x);left = 0;}
    bool operator < (const Line& L) const {
        return ang < L.ang;
    }
    Vec getV(){
        return in[P2]-in[P1];
    }
    Point getP(){
        return in[P1];
    }
};
vector fuck;
Line temp[1005];
bool OnLeft(Line L,Point P){
    return dcmp(Cross(L.getV(),P - L.getP()))>=0;          //如果线上的点不算就改成>
}
Point GetIntersection(Line& a, Line& b){
    Vec u = a.getP() - b.getP();
    double t = Cross(b.getV(),u)/Cross(a.getV(),b.getV());
    return a.getP()+a.getV()*t;
}
int HalfplaneIntersection(vector& L,vector& poly){
    int n = L.size();
    sort(L.begin(),L.end());
    int first,last;
    Point *p = new Point[n];
    Line *q = new Line[n];
    q[first = last = 0] = L[0];
    for(int i=1;iwhile(first1]))last--;
        while(firstif(fabs(Cross(q[last].getV(),q[last-1].getV()))if(OnLeft(q[last],L[i].getP()))q[last] = L[i];
        }
        if(first1] = GetIntersection(q[last-1],q[last]);
    }
    while(first1]))last -- ;
    if(last-first<=1){
        delete[] p;
        delete[] q;
        return 0;
    }
    p[last] = GetIntersection(q[last],q[first]);
    int m = 0;
    for(int i=first;i<=last;i++)poly.push_back(p[i]),m++;
    delete[] p;
    delete[] q;
    return m;
}
/*=========半平面交所需函数及主过程=========*/
int main(){
    //freopen("in.txt","r",stdin);
    int T;
    cin>>T;
    for(int cas = 1;cas <= T;cas++){
        vector poly;
        scanf("%d",&n);
        double x,y;
        poly.clear();
        fuck.clear();
        for(int i=0;iscanf("%lf%lf",&x,&y);
            in[i] = Point(x,y);
        }
        int limit = (int)floor(n/3.0);
        for(short i=0;i0;
            queue q;
            for(short j=0;jif(i!=j)
                    temp[tcnt++] = Line(i,j);
            sort(temp,temp+tcnt);
            int len = tcnt,u = 0;
            for(int k=0;kif(k){
                    q.pop();
                    u--;
                }
                for(;dcmp(Cross(temp[k].getV(),temp[(k+u)%len].getV()))>=0 && u for(int k=0;kif(n - temp[k].left - 1  < limit)
                    fuck.push_back(temp[k]);
            }
            //for(int k=0;k
            //    Point a = fuck[k].P,b = a+fuck[k].v;
            //    printf("x1: %f y1: %f x2:%f y2:%f\n",a.x,a.y,b.x,b.y);
            //}
            //printf("%d\n",fuck.size());
        }
        int ok = HalfplaneIntersection(fuck,poly);
        printf("Case #%d: %.6f\n",cas,ok?PolygonArea(poly):0);
    }
    return 0;
}

I Inverse

题意

给出一组a[i],可以根据某种规则,把这个数组加密成b[i]。然后给出b[i],要求求出所有a[i]

思路

比赛时利用__builtin_parity()打表,发现给定的矩阵是一个递归构成的矩阵,把这个矩阵等分成四份,右上角是-x子矩阵,其他的三份都是x子矩阵。a*[] = b,所以我们只要找到一种操作,利用行初等变换把矩阵变换成单位矩阵,这样b经过同样的变换后得到的就是a。
找规律大法好!jiefangxuanyan线代学的就是好!

AC代码

HDU5244

/*************************************************************************
    > File Name: pi.cpp
    > Author: jiefangxuanyan
    > Created Time: 四  5/28 12:32:42 2015
 ************************************************************************/

#include 
long long in[1<<20];
void sp(long long *t,int n){
    if(n==1){
        return;
    }
    int m=n>>1;
    for(int i=0;ifor(int i=0;ifor(int i=0;i1];
    }
    for(int i=0;i>=1;
    }
    for(int i=0;i/*for(int i=0;i
    sp(t,m);
    sp(t+m,m);
}
int main(){
    int cn;
    scanf("%d",&cn);
    for(int ci=1;ci<=cn;ci++){
        int k;
        scanf("%d",&k);
        int n=1<for(int i=0;i"%I64d",in+i);
        }
        sp(in,n);
        printf("Case #%d:",ci);
        for(int i=n-1;i>=0;i--){
            printf(" %I64d",in[i]);
        }
        putchar('\n');
    }
    return 0;
}

J Joyful

题意

在一个n*m的矩阵上随机选取k个小的矩形区域覆盖,求被覆盖的格子的数量的数学期望。随机是指从矩阵中随机选取两个格子,作为矩形的两个顶角。

思路

这题的数学期望等于每个格子被覆盖的概率之和。所以只需要求每个格子被覆盖的概率即可。可以算出,一次随机覆盖,每个格子不被覆盖的概率p,这样,1-p^k就是这个格子被覆盖的概率。求和即可。

AC代码

HDU 5245

/*************************************************************************
    > File Name: pj.cpp
    > Author: znl1087
    > Mail: [email protected] 
    > Created Time: 三  5/27 04:44:07 2015
 ************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

int main(){
    int T;
    cin>>T;
    int m,n,k;
    for(int cas = 1;cas <= T;cas++){
        scanf("%d%d%d",&m,&n,&k);
        double ans = 0;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++){
                double t = (1.0 - ((i-1)*(i-1)+(m-i)*(m-i)) /(double) (m * m))
                        * (1.0-((j-1) * (j-1) + (n-j) * (n-j)) / (double)(n * n));
                //printf("%lf\n",t);
                ans += 1.0-pow(1.0-t,(double)k);
            }
        //printf("%f\n",ans);
        printf("Case #%d: %d\n",cas,(int)round(ans));
    }
    return 0;
}


题目评价

B题E题是水题,F题是一个脑筋急转弯,A题和J题是概率题,G题是一道不错的贪心,CD应该都是带一些数论的线段树。。I题是找规律加矩阵初等变换。。。 H题没看,不知道是啥,全场只有AK队过了。 H题就是个计算几何,半平面交,比赛时候不限内存。。要是直接做应该能过。。是我没读题。。总体来说,数学题偏多,题目质量还可以。基本上能做出J题的都能拿到奖,G题是铜牌和银牌的分界题,A,C,D,I,任意做出两个就是金牌。(AK队太叼,不敢评价)。1个AK,7个八题,9个七题,都是金牌。我们队这次基本都是1Y,自我感觉还是可以的。中间空了一个小时的键盘是硬伤,需要继续努力训练,并且好好总结自己的问题。

总之,拿到金牌,拿到名额,任务还是完成了的,吃了一顿大餐,感觉还是不错的!

你可能感兴趣的:(比赛总结)