COCI模拟考试心得体会

今天考了一场COCI的比赛,感觉题目没考什么特别难的算法,但是有些题就实现很麻烦。


第一题键盘打字,就是个打表题,打了我好大半天,还好没打错。


第二题一群固执的老人看电视,模拟一下谁去换台,换了什么台就可以过了,注意有循环就输出-1


第三题building,一道卡格式的题,给出每个矩形的左下角右上角坐标,让你勾勒整个透视图的轮廓。我想的是从外围跑一次flood fill,能够接触到的矩形的边就是要画出来的了。然后输出的时候注意下标迭代的顺序。这么一道无聊的题花了我好大半天时间,写了100行才写完。


第四题ranking,就是绑定矩形前缀和,然后利用矩形割补法求一下这个人能“完虐”多少人,多少人能“完虐“他,其他的人总是存在合理的安排顺序的,然后注意一下细节就行了。


第五题堆栈,比较有意思,所有的操作要么是复制一个之前出现过的堆栈,然后把当前操作id放在栈顶,要么复制一个之前的堆栈,然后pop,要么比较两个堆栈有几个公共元素。首先明确模拟肯定没法做,因为每一次复制都会增加O(n)的内存,内存很容易就崩了。模拟一下样例就会发现栈里的元素都是递增的,那么建一棵trie树,把每一操作后新产生的堆栈对应到里面的节点即可,公共元素的操作就显然了,在上面做LCA即可。


第五题。在数轴上有许多城市,相邻两城市距离为1,N辆卡车,每辆途经Ki个城市并给出这Ki个城市。M次询问,每次给出两个城市,问这两辆车迎面相遇多少次,相遇时其中一个车正在掉头或刚好结束旅途不算。N<=100000, M<=300000, Ki之和<300000.

先记录所有掉头的时间并按时间先后排序,对于两辆卡车,若他们在相邻两个事件发生时的左右关系不同,则相遇次数+1,每次询问如果扫描一遍这个数组,可以得50分。

显然可以将询问绑定在其中一个车子的事件上,具体做法是选择Ki较小的绑定。这样做的时间复杂度为Msqrt(N)。证明如下:

若该车的Ki >= sqrtN,只有Kj大于Ki的卡车才会放入,则最多有N / Ki <= sqrtN个询问放在这个事件对应的链表里。

若该车的Ki < sqrtN,则这个车出现在事件数组中的次数=Ki

从这个分析看出最坏的复杂度为(M+N)sqrtN,但是显然这题不可能所有车都处于最坏状态,所以3s应该还是比较宽松的。

具体实现过程中需要扫描事件数组,并实时更新每个车的状态(城市,方向),我自己写得很不科学,最后是按着标程的方法实现的,标程中用map来标记重复的询问,我省略了,加上那个判重大概快300ms。

正常题的解决模式应该是:理解题意,确定方向,对比数据范围,确定大致复杂度,设计算法,实现。

这类题比较特殊,解决模式是:理解题意,可行化,优化,检验复杂度,实现。以后应注意这类问题的操作模式。

#include
#include
#include
#include
#include
#include
using namespace std;
#define LL long long 
#define MP make_pair
#define pii pair 
int N, M;

inline void get(int&r) {
    char c; r = 0;
    do c=getchar(); while (c<'0'||c>'9');
    do r=r*10+c-'0', c=getchar(); while (c>='0'&&c<='9');
}
const int MAXN = 100005, MAXM = 300005;

int ans[MAXM];
int evn;
struct event {
    int id, x, dir;
    LL t;
    event() {}
    event(int a, int b, int c, LL d) {
        id = a; x = b; dir = c; t = d;
    }
    bool operator < (const event&b) const {
        if (t != b.t) return t < b.t;
        return dir > b.dir;
    }
} ev[MAXM];
 
struct Query {
    int id, left, loc;
    Query () {}
    Query (int a, int b, int c) { id=a; left=b; loc=c; }
};
vector as[MAXN];
 
int Ki[MAXN], xi[MAXN], dir[MAXN];
LL tm[MAXN];

void apply(const event &e)
{
    xi[e.id] = e.x;
    dir[e.id] = e.dir;
    tm[e.id] = e.t;
}

int checkleft(int a, int b, LL pret, int pred)
{
    if (dir[b] == 0 && pret >= tm[b]) return 0;
    int bpos, apos;
    if (dir[b] == 0) {
        bpos = xi[b];
        apos = (tm[a] - tm[b]) * (-pred) + xi[a];
    }
    else {
        apos = xi[a];
        bpos = (tm[a] - tm[b]) * dir[b] + xi[b];
    }
    if (bpos < apos) return -1;
    return 1;
}

void solve(int id, Query&q, LL pret, int pred)
{
    int isleft = checkleft(id, q.id, pret, pred);
    ans[q.loc] += (q.left * isleft == -1);
    q.left = isleft;
}

int main()
{
    get(N), get(M);
    LL cur;
    int i, j, x, x1, a, b;
    for (i = 1; i<=N; ++i)
    {
        cur = 0;
        get(Ki[i]); get(x);
        for (j = 1; j < Ki[i]; ++j) {
			get(x1);
			event e = event(i, x, x Ki[b]) swap(a, b);
		as[a].push_back( Query(b, xi[b] < xi[a] ? -1 : 1, i) );
	}
	sort(ev+1, ev+evn+1);
    for (i = 1; i<=evn; ++i)
    {
		LL pret = tm[ev[i].id];
		int pred = dir[ev[i].id];
		apply(ev[i]);
		for (j = 0; j < (int)as[ev[i].id].size(); ++j)
			solve(ev[i].id, as[ev[i].id][j], pret, pred);
    }
    for (i = 1; i<=M; ++i)
		printf("%d\n", ans[i]);
    return 0;
}



你可能感兴趣的:(学习日志)