2018-2019 ACM-ICPC, Asia Seoul Regional Contest——刷题计划

A - Circuits

  • 解题思路: 本题研究一下数据就可以发现,可以无视矩形,只看平行于y轴的边一条边即可,于是我们可以把这个题目转换为,两条直线和若干条线段相交的问题。我们设直线为 y = a , y = b y=a,y=b y=a,y=b我们枚举 a a a y = a y=a y=a 相交了多少条线段,可以通过差分直接求出。然后我们将a经过的线段全部删除,在剩下的数组中找到最大的与 y = b y=b y=b相交的线段数即可。这里我们倒叙枚举端点,使用线段树来维护寻找 y = b y=b y=b相交多少条线段的过程。
const int N = 2e5 + 5;
int n;
int L[N], R[N], A[N], cnt;
int c[N];
vector <int> ve[N];
struct node {
    int tag, maxn;
}tr[N << 2];
void build (int i , int l , int r)
{
    tr[i].maxn = tr[i].tag = 0;
    if (l == r)
        return ;
    int mid = (l + r) >> 1;
    build (i << 1, l , mid);
    build (i << 1 | 1, mid + 1 , r);
}
void push_down (int i)
{
    if (tr[i].tag)
    {
        tr[i<<1].maxn += tr[i].tag;
        tr[i<<1|1].maxn += tr[i].tag;
        tr[i<<1].tag += tr[i].tag;
        tr[i<<1|1].tag += tr[i].tag;
        tr[i].tag = 0;
        return ;
    }
}
void update (int i , int l , int r, int L , int R)
{
    if (L <= l && R >= r)
    {
        tr[i].maxn += 1;
        tr[i].tag += 1;
        return ;
    }
    push_down(i);
    int mid = (l + r) >> 1;
    if (L <= mid) update (i<<1 ,l , mid, L, R);
    if (R > mid) update (i<<1|1, mid+1 ,r , L, R);
    tr[i].maxn = max (tr[i<<1].maxn, tr[i<<1|1].maxn);
}
int main()
{
    CLOSE;
    cin >> n;
    for (int i = 1 ; i <= n ; i ++)
    {
        int x1, x2, y1, y2;
        cin >> x2 >> y2 >> x1 >> y1;
        L[i] = y1, R[i] = y2;
        A[++ cnt] = y1;
        A[++ cnt] = y2;
    }
    sort (A + 1 , A + 1 + cnt);
    int len = unique (A + 1 , A + 1 + cnt) - A - 1;
    for (int i = 1 ; i <= n ; i ++)
    {
        L[i] = lower_bound(A + 1 , A + 1 + len, L[i]) - A;
        R[i] = lower_bound(A + 1 , A + 1 + len, R[i]) - A;
        ve[L[i]].pb (R[i]);
    }
    for (int i = 1 ; i <= n ; i ++)
        c[L[i]] += 1, c[R[i] + 1] -= 1;
    for (int i = 1 ; i <= len ; i ++)
        c[i] += c[i-1];
    int ans = 0;
    build (1, 1, len);
    for (int i = len ; i >= 1 ; i --)
    {
        ans = max (ans, c[i] + tr[1].maxn);
        for (auto j : ve[i]) {
            update(1, 1, len, i, j);
        }
    }
    cout << ans << endl;

}

D - Go Latin

  • 解题思路: 模拟一下就行
int n;
string str;
int main()
{
    cin >> n;
    for (int i = 1 ; i <= n ; i ++)
    {
        cin >> str;
        int len = str.length();
        if (str[len-1] == 'a')
            str += 's';
        else if (str[len-1] == 'i' || str[len-1] == 'y')
        {
            str[len-1] = 'i';
            str += "os";
        }
        else if (str[len-1] == 'l')
            str += "es";
        else if (str[len-1] == 'n' || (str[len-1] == 'e' && str[len-2] == 'n'))
        {
            if (str[len-1] == 'n')
                str[len-1] = 'a', str += "nes";
            else
                str[len-2] = 'a', str[len-1] = 'n', str += "es";
        }
        else if (str[len-1] == 'o')
            str += 's';
        else if (str[len-1] == 'r')
            str += "es";
        else if (str[len-1] == 't')
            str += "as";
        else if (str[len-1] == 'u')
            str += "s";
        else if (str[len-1] == 'v')
            str += "es";
        else if (str[len-1] == 'w')
            str += "as";
        else
            str += "us";
        cout << str << endl;
    }
}

L - Working Plan

  • 解题思路: 通过简单思考之后可以发现,如果我们每次使用剩余工作天数最多的人去工作的话可以最大概率去完成任务,利用优先队列贪心模拟一下即可
const int N = 2005;
int n, m, w, h;
int a[N], b[N], mark[N];
int pre[N];
int vis[N][N];
struct node {
    int val,id;
    bool operator < (const node& no) const {
        return val < no.val;
    }
};
p_queue <node> q;
int main()
{
    cin >> m >> n >> w >> h;
    for (int i = 1 ; i <= m ; i ++)
        pre[i] = -2005;
    for (int i = 1 ; i <= m ; i ++)
        cin >> a[i];
    for (int i = 1 ; i <= n ; i ++)
        cin >> b[i];
    int ok = 1;
    for (int i = 1 ; i <= n ; i ++)
    {
        for (int j = 1 ; j <= m ; j ++)
            if (a[j] > 0 && !mark[j] && i - pre[j] - 1 >= h)
                q.push ({a[j],j}), mark[j] = 1;
        int res = b[i];
        while (!q.empty())
        {
            if (res == 0)
                break;
            auto x = q.top();
            q.pop();
            mark[x.id] = 0;
            vis[x.id][i] = 1;
            pre[x.id] = i + w - 1;
            a[x.id] -= w;
            res --;
            for (int j = i ; j <= i + w - 1 ; j ++)
                b[j] --;
        }
        if (res)
        {
            ok = 0;
            break;
        }
    }
    if (!ok)
    {
        cout << -1 << endl;
        return 0;
    }
    cout << 1 << endl;
    for (int i = 1 ; i <= m ; i ++)
    {
        for (int j = 1 ; j <= n ; j ++)
            if (vis[i][j])
                cout << j << " ";
        cout << endl;
    }
}

K - TV Show Game

  • 解题思路: 通过读题,我们转换问题,可以发现,如果三个指派中有一个为假,那么其他两个一定为真,我们根据这个建立出有向图之后2-SAT即可
const int N = 4e5 + 5;
int n , m;
int head[N] , cnt;
struct node {
    int t, next;
}edge[N << 1];
void add (int f, int t)
{
    edge[cnt].t = t;
    edge[cnt].next = head[f];
    head[f] = cnt ++;
}
int times , tot ;
int dfn[N] , low[N] , vis[N] , st[N] , scc[N] , type = 0;
void tarjan (int u)
{
    dfn[u] = low[u] = ++ times;
    st[++ tot] = u;
    vis[u] = 1;
    for (int i = head[u] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].t;
        if (!dfn[v])
        {
            tarjan(v);
            low[u] = min (low[u], low[v]);
        }
        else if (vis[v])
            low[u] = min (low[u] , dfn[v]);
    }
    if (low[u] == dfn[u])
    {
        type ++;
        do {
            scc[st[tot]] = type;
            vis[st[tot]] = 0;
            tot --;
        }while (u != st[tot + 1]);
    }
    return ;

}
int x[5], y[5];
int main()
{
    CLOSE;
    mem (head, -1);
    cin >> n >> m;
    for (int i = 1 ; i <= m ; i ++)
    {
        char op;
        for (int j = 1 ; j <= 3 ; j ++)
        {
            cin >> x[j] >> op;
            if (op == 'B')
                y[j] = 1;
            else
                y[j] = 0;
        }
        add (x[1] + (1 - y[1]) * n, x[2] + n * y[2]);
        add (x[1] + (1 - y[1]) * n, x[3] + n * y[3]);
        add (x[2] + (1 - y[2]) * n, x[1] + n * y[1]);
        add (x[2] + (1 - y[2]) * n, x[3] + n * y[3]);
        add (x[3] + (1 - y[3]) * n, x[1] + n * y[1]);
        add (x[3] + (1 - y[3]) * n, x[2] + n * y[2]);
    }
    for (int i = 1 ; i <= 2 * n ; i ++)
        if (!dfn[i])
            tarjan (i);
    for (int i = 1 ; i <= n ; i ++)
    {
        if (scc[i] == scc[i + n])
        {
            cout << -1 << endl;
            return 0;
        }
    }
    for (int i = 1 ; i <= n ; i ++)
    {
        if (scc[i] < scc[i + n])
            cout << 'R';
        else
            cout << 'B';
    }
    cout << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值