HNOI2018

Day1

Day2

游戏

https://www.luogu.org/problemnew/show/P4436

前二十分暴力

对于 y <= x 的数据,只要门被上锁,便无法从右向左,从1~n扫一遍很容易得到每个点能遍历的区间的左端点

因为所有钥匙都在门左侧,那么从1一定能走到n,初始 l=1, r=n

再依次枚举起点s,若无法向左走到l,则需要l ~ i - 1这段路上的钥匙的门也无法通过,更新r

考虑到若 rmin< s ,则当前 r 对 s 对应的区间已无限制, r 应取大于 s 的最小值这里我是用堆维护

剩下40分,坑先挖着吧,以后来填

20 + 40 分代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000005

int n, m, Q;
int key[N], ans[N][2];
vector<int> sum[N];
priority_queue<int, vector<int>, greater<int> > q;

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

int main()
{
    n = read(); m = read(); Q = read();
    bool casexy = true;
    for (int i = 1; i <= m; i++)
    {
        int x = read(), y = read();
        key[x] = y;
        if (x != y) sum[y].push_back(x);
        if (x < y) casexy = false;
    }
    if (casexy)
    {
        int p = 1;
        for (int i = 1; i <= n; i++)//先处理出左端点
        {
            ans[i][0] = p;
            if (key[i]) p = i + 1;
        }
        int l = 1, r = n; q.push(n);
        for (int i = 1; i <= n; i++)
        {
            while(r < l && !q.empty())
            {
                q.pop(); r = q.top();
            }
            if(r < i) {ans[i][1] = i; continue;}
            ans[i][1] = r;
            while (ans[i + 1][0] > l)
            {
                for(int j = 0; j < sum[l].size(); j++)
                {
                    r = min(r, sum[l][j]);
                    q.push(sum[l][j]);
                }
                l++;
            }
        }
        while (Q--)
        {
            int x = read(), y = read();
            if (ans[x][0] <= y && ans[x][1] >= y) puts("YES"); else puts("NO");
        }
    }
    else
    {
        for(int i = 1; i <= n; i++) ans[i][0] = ans[i][1] = i;
        for(int s = 1; s <= n; s++)
        {
        	 int l = s, r = s;
        	 while(l > 1 || r < n)
        	 {
        	 	if(l > 1 && (!key[l - 1] || (key[l - 1] >= l && key[l - 1] <= r))) l = ans[l - 1][0];
        	 	else if(r < n && (!key[r] || (key[r] >= l && key[r] <= r))) r = ans[r + 1][1];
        	 	else break;
        	 }
        	 ans[s][0] = l; ans[s][1] = r;
        }
        while(Q--)
        {
        	int x = read(), y = read();
        	if (ans[x][0] <= y && ans[x][1] >= y) puts("YES"); else puts("NO");
        }
    }
    return 0;
}

道路

https://www.luogu.org/problemnew/show/P4438

考场上觉得暴搜打那么多只有20分太不划算,然而同行julao说暴搜有70

喵喵喵???

然后更加ju的julao传授了dp 40行AC代码

我果然还是太菜了啊

#include <bits/stdc++.h>
using namespace std;
#define N 20005
#define M 45
#define inf 1000000000000000
#define ll long long

int n;
int s[N], t[N];
int a[N], b[N], c[N];

ll f[M][M];//f[i][j]表示从当前节点向上有i条公路,j条铁路待修
void dfs(int x, ll T[M][M])
{
    if(x < 0)//所有乡村均为叶子节点
    {
        x *= -1;//节点暴力枚举所有情况
        for(int i = 0; i <= 40; i++)
            for(int j = 0; j <= 40; j++)
                T[i][j] = (ll)c[x] * (a[x] + i) * (b[x] + j);
        return;
    }
    ll l[M][M], r[M][M];
    dfs(s[x], l); dfs(t[x], r);
    for(int i = 0; i <= 40; i++)//f[x][i][j] 省去一维
        for(int j = 0; j <= 40; j++)
            T[i][j] = min(l[i][j] + r[i][j + 1], l[i + 1][j] + r[i][j]); //选择公路或铁路进行翻修
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i < n; i++) scanf("%d%d", &s[i], &t[i]);//二叉树
    for(int i = 1; i <= n; i++) scanf("%d%d%d", &a[i], &b[i], &c[i]);
    dfs(1, f);
    printf("%lld", f[0][0]);
    return 0;
}
posted @ 2018-04-16 10:49  XYZinc  阅读(175)  评论(0编辑  收藏  举报