【NOIP模拟】20151014模拟

8 篇文章 0 订阅
7 篇文章 0 订阅

T1 外星人的友情

Problem

问题描述

小 Y 最近正在接受来自 X3 星球的外星人的采访。在那个星球上,每个人的名字都是一个正整数。所有在这个星球上的居民都是相互认识的。两个这个星球上的人的友谊值可以用这样来计算:先把两个人的名字转换成二进制,然后把他们上下对齐排好,如果同一列的值相等,那么相应列的值就是 0,否则的话就是1,结果最后仍旧转换成十进制数。
例如,两个人的名字是 19 和 10,他们的友谊值是 25
这个星球的价值是这么计算的:所有友情值的和。
小 Y 邀请你帮助他来计算
这个值。

输入

输入文件第一行一个整数 N,表示这个星球上的总人口。
接下来 N 行,每行一个正整数(小于 1000000),表示每个居民的姓名。

输出

输出文件一行一个整数,表示这个星球的价值。

输入输出样例 1

alien.in
2
25
19
alien.out
10

输入输出样例 2

alien.in
3
12
7
3
alien.out
5

输入输出样例 3

alien.in
5
84
9
13
1
9
alien.out
6

样例说明

在第 2 个样例中,1 和 2 的友谊值是 4,1 和 3 的友谊值是 2,2 和 3 的友谊值
是 6,所以结果是 4+2+6=12。

数据限制

对于 40%的数据满足:N<=5000
对于 70%的数据满足:N<=100000
对于 100%的数据满足:1<=N<=1000000,每个居民的姓名小于 1000000

Solution

先逐位统计每一位上的1的个数和0的个数,这里注意位数不满的要补齐前导零并计数之
然后用乘法原理统计每一位的异或和,累加

Code

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define red(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long

const int N = 1100000;
int n; ll ans;
ll a[N];
ll base[50], cnt[50][3];

inline ll read() {
    ll x = 0; char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x;
}

void calc(ll x) {
    int j = 0;
    while(x) {
        cnt[++j][x & 1]++;
        x >>= 1;
    }
    for(j = j + 1; j <= 22; j++) cnt[j][0]++;
}

int main() {
    freopen("alien.in", "r", stdin);
    freopen("alien.out", "w", stdout);

    scanf("%d", &n);
    memset(cnt, 0, sizeof(cnt));
    memset(base, 0, sizeof(base));
    rep(i, 1, n) {
        a[i] = read();
        calc(a[i]);
    }
    base[1] = 1;
    rep(i, 2, 22) base[i] = base[i - 1] * 2ll;
    ans = 0;
    rep(i, 1, 22) ans += cnt[i][0] * cnt[i][1] * base[i];
    printf("%I64d\n", ans);
    return 0;
}

T2 多段线

Problem

问题描述

有若干个类似于下面的函数:
这里写图片描述
定义 n 个函数 y1(x),…,yn(x)的对于任意 x 的总和 s(x)=y1(x)+...+yn(x)
很容易发现 s(x)的图象是多段线组成。给你 n 个函数,你的任务是找出 s(x)图象不等于 180 度的角的个数。

输入

第一行一个整数 n,表示函数的个数。
接下来 n 行,每行包含两个空格隔开的整数 ki,bi,表示第 i 个函数的参数。

输出

输出一行一个整数,表示形成的多段线的图像中不等于 180 度角的个数。

输入输出样例

polyline.in
3
1 0
0 2
-1 1
polyline.out
2

数据限制

对于 30%的数据满足:n<=3000
对于 100%的数据满足:1<=n<=100000,-10^9 <= ki,bi <= 10^9

Solution

显然的, s(x) 是由多条直线构成的,因此若有夹角出现,则一定是斜率发生了变化
再考虑写率变化的方式,由于单个一次函数在大于0时有斜率,小于0时则没有。因此 s(x) 的每一次斜率变化一定发生在某一个一次函数的零点
现在已经可以解决这个题了,记一个sk,表示图像的当前斜率,将零点按大小排序,在每个零点进行修改
诶嗨,这个sk好像是单调的呢
为什么呢?很简单的啦,自己想吧~
也就说,只要统计有多少个不同的零点就可以了

Code

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define red(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define ld long double

const int N = 110000;
struct Line {
    ll k, b;
    ld x0;
}line[N];
int n, ans = 0;

inline ll read() {
    ll x = 0, f = 1; char c = getchar();
    while(!isdigit(c)) {
        if (c == '-') f = -1;
        c = getchar();
    }
    while(isdigit(c)) {
        x = x * 10ll + c - '0';
        c = getchar();
    }
    return x * f;
}

bool cmp(Line x, Line y) { 
    return (x.k * y.k > 0 && x.b * y.k - y.b * x.k > 0 ||
            x.k * y.k < 0 && x.b * y.k - y.b * x.k < 0);
}

bool dcmp(Line x, Line y) {
    return x.b * y.k - y.b * x.k == 0;
}

int main() {
    freopen("polyline.in", "r", stdin);
    freopen("polyline.out", "w", stdout);

    n = read();
    ll sk = 0;
    int j = 0;
    rep(i, 1, n) {
        int kk = read(), bb = read();
        if (kk == 0) continue;
        line[++j].k = kk; line[j].b = bb;
        if (line[j].k < 0) sk += line[j].k; 
    }
    n = j;
    sort(line + 1, line + n + 1, cmp);
    int i = 1;
    while(i <= n) {
        ll old = sk;
        while(dcmp(line[i], line[i + 1]) && i < n) {
            if (line[i].k < 0) sk -= line[i].k;
            else sk += line[i].k;
            i++;
        }
        if (line[i].k < 0) sk -= line[i].k;
        else sk += line[i].k;
        if (sk != old) ans++;
        i++;
    }
    printf("%d\n", ans);
    return 0;
}

T3 奇葩的路

Problem

问题描述

现在有一条单个车道的路,两边会不断有车来,任意时刻路上只能有一个方向的车,每辆车速度相同,同一方向的车必须在前一辆车上路 3 秒后才能上路,可以在路口等待,不能超车,每个人在路口等过其忍受范围后便会不高兴,现在请你安排过路顺序,使得不高兴的人数最少。

输入

第一行两个整数 t 和 n,4 ≤ t ≤ 180 ,1 ≤ n ≤ 250,代表有 n 辆车,
每辆车过桥时间为 t 秒;
下面 n 行,描述每辆车,每行一个字符 c,两个数字 a 和 r;c=“E”或“W”
代表方向,a 代表到达时间(秒),r 代表最大忍受时间(秒,上路时间 - a),
0 ≤ a < 86400,0 ≤ r ≤ 3600),按到来顺序给出,a 相同时以到来顺序为
准。

输出

输出一行一个整数,代表最少发怒人数。

输入输出样例

roadwork.in
100 5
W 0 200
W 5 201
E 95 1111
E 95 1
E 95 11
roadwork.out
1

Solution

dp[left][right][k][tag] 表示从左向右过了left辆车,从右向左过了right辆车,其中最后一辆车方向为tag(0表示从左向右,1表示从右向左),导致k个人不开心的最短时间
分类讨论,可以O(1)转移

Code

// Solution of problem Road Work
// Time complexity: O(n^3)
// Space complexity: O(n^2)
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
using namespace std;

#define MAX_N 350
int resetValue = 123*MAX_N;
const int maxTotalTime = 86400+MAX_N*3+2*MAX_N*180;

struct Car
{
    int arrival;
    int lastLeave;
};
vector<Car> toWest;
vector<Car> toEast;
int timeToPass;

int dp[MAX_N+1][MAX_N+1][MAX_N+1][2]; // totalTime = dp[drivenEast][drivenWest][numIrritated][lastToWest=0,lastToEast=1]

int calc(int drivenWest, int drivenEast, int numIrritated, int lastToEast)
{
    int &dpv = dp[drivenWest][drivenEast][numIrritated][lastToEast];
    if (dpv == 0x7f7f7f7f)
    {
        if ((lastToEast ? drivenEast : drivenWest) == 0)
            dpv = maxTotalTime;
        else
        {
            const Car &myCar = (lastToEast ? toEast[drivenEast-1] : toWest[drivenWest-1]);
            if (lastToEast)
                --drivenEast;
            else
                --drivenWest;
            dpv = maxTotalTime;
            // Check with one less to the east and one less to the west.
            for (int dir = 0; dir < 2; ++dir)
            {
                // Allow getting irritated
                if (numIrritated > 0)
                    dpv = min(dpv, calc(drivenWest, drivenEast, numIrritated-1, dir) + (dir == lastToEast ? 3 : timeToPass));
                // Not getting irritated
                int myTime = calc(drivenWest, drivenEast, numIrritated, dir) + (dir == lastToEast ? 3 : timeToPass);
                if (myTime <= myCar.lastLeave)
                    dpv = min(dpv, myTime);
            }
            if (dpv < maxTotalTime)
                dpv = max(myCar.arrival, dpv);
        }
    }
    return dpv;
}

int main()
{
    freopen("roadwork.in","r",stdin);
    freopen("roadwork.out","w",stdout);
    int n;
    cin >> timeToPass >> n;
    for (int i = 0; i < n; ++i)
    {
        string d;
        int maxStillTime;
        Car car;
        cin >> d >> car.arrival >> maxStillTime;
        car.lastLeave = car.arrival + maxStillTime;
        if (d == "W")
            toWest.push_back(car);
        else if (d == "E")
            toEast.push_back(car);
        else
            return -1;
    }
    // Dynamic programming
    // Reset
    memset(&dp[0][0][0][0], 0x7f7f7f7f, sizeof(dp));
    dp[0][0][0][0] = dp[0][0][0][1] = -3;
    // Run
    int numIrritated;
    for (numIrritated = 0; numIrritated <= n; ++numIrritated)
    {
        int t = min(
            calc((int)toWest.size(), (int)toEast.size(), numIrritated, 0),
            calc((int)toWest.size(), (int)toEast.size(), numIrritated, 1));
        if (t < maxTotalTime)
            break;
    }
    cout << numIrritated << endl;
    return 0;
}

尾声

前两题水题,最后一题也不是很难,题目描述太蛋疼。
最后一题强行意淫了一个平方算法,自认为很精妙的WA了。
还是要按照基本法来。
220,考得还没以前的小朋友高,不过还是rank1。。说什么呢
最后一题的std是java风格-_-||
HBH一发自爆,大概炸来了一个妹纸吧
SXP需要大量的蛋白质

End.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值