LOADING

加载过慢请开启缓存 浏览器默认开启

2016 USP-ICMC

Problem - B - Martian Sunrise贪心

题目描述:

给你mm行音调,每行音调有77个元素,给你一行目标音调nn , 每次可以从mm行任选两行,作为你的元素集合去匹配目标音调,求最少多少次完成匹配。1m161n1e4(1 \le m \le 16,1 \le n \le 1e4)

题目分析:

所有选择的方案总共m(m1)/2m*(m-1)/2,乘上总元素大约30003000,然后直接从第一个目标音调的元素枚举到nn,同时枚举所有现存的包含当前元素的集合,同时去除不包含的,如何此时没有任何一个集合满足,则重新枚举所有集合并且答案数++++。这里有个贪心正确性:取匹配目标元素最长的集合并不会让总集合数变多。时间复杂度大约3e73e7左右。

题目代码:

map<string, int> mp;
int cnt;
set<int> b[300];
bool st[300];
void solve()
{
    int m;
    cin >> m;
    vector<int> v[m];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < 7; j++)
        {
            string s;
            cin >> s;
            if (!mp[s])
            mp[s] = ++cnt;
            v[i].pb(mp[s]);
        }
    }
    
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto &x : a)
    {
        string s;
        cin >> s;
        x = mp[s];
    }
    
    int idx = 0;
    if (m == 1)
    {
        for (auto &x : v[0])
            b[idx].insert(x);
        idx++;
    }
    
    for (int i = 0; i < m; i++)
    {
        for (int j = i + 1; j < m; j++)
        {
            for (auto &x : v[i])
                b[idx].insert(x);
            for (auto &x : v[j])
                b[idx].insert(x);
            idx++;
        }
    }
    
    memset(st, 0, sizeof st);
    int num = idx, ans = 1;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < idx; j++)
        {
            if (!st[j] && b[j].find(a[i]) == b[j].end())
                num--, st[j] = 1;
            if (!num)
            {
                i--;
                break;
            }
        }
        if (!num)
            num = idx, ans++, memset(st, 0, sizeof st);
    }
    
    cout << ans << "\n";
}

Problem - I. Lazy Painting/DFS/SET(stl)

题目描述:

实验室地板可以表示为一个 NN 行、MM 列的矩阵,总共有 N×MN \times M 块瓷砖。指令是以矩形区域为单位进行涂漆,每个矩形的高度为 HH,宽度为 WW,且这些矩形可以重叠。每次指令后,需要计算还有多少瓷砖未涂漆。

马加里达负责执行指令,她会检查矩形的左边界,如果左侧边框中的瓷砖都已经涂漆,她会跳过该矩形。如果有未涂漆的瓷砖,她会将从左侧边框可到达的所有未涂漆的瓷砖进行涂漆。如果只有矩形内的未涂漆瓷砖可以到达某个瓷砖,那么这个瓷砖也会被涂漆。

输入

第一行包含五个整数 NNMM (1N,M105(1 ≤ N, M ≤ 10^51NM3106)1 ≤ N * M ≤ 3 * 10^6)HHWW (1HN(1 ≤ H ≤ N1WM)1 ≤ W ≤ M)QQ (1Q105)(1 ≤ Q ≤ 10^5). 然后是 QQ 行. 第 ii 行包含两个整数 rir_icic_i (1riNH+1(1 ≤ r_i ≤ N - H + 11ciMW+1)1 ≤ c_i ≤ M - W + 1)—要绘制的矩形区域的左上平铺. 研究实验室最左边最上面的瓦片是 (1,1)(1, 1).

输出

每次指令过后有多少未覆盖的瓷片.

题目分析:

因为是检查左边界,用setset记录每列还有哪些没有被覆盖。当执行命令的时候二分查找还存在的,加到队列中进行DFS/BFSDFS/BFS覆盖即可,同时维护答案–。因为整个询问 qq 次的时候,矩阵只会被访问一次 ,每次访问的时候set删除 logn\log n,所以总复杂度约为 O((Q+N×M)logn)O((Q+N \times M) \log n).

题目代码:

int n, m, h, w, Q, ans, dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
set<int> s[N];
queue<PII> q;
bitset<M> st;
void bfs(int u, int v)
	{
		int nn = u + h - 1, mm = v + w - 1;
		set<int>::iterator it = s[v].lower_bound(u);
		// cout<<u<<" "<<*it<<" "<<nn<<" "<<mm<<"\n";
		while (*it <= nn)
		{
			q.push({*it, v});
			// cout<<*it<<"\n";
			st[(*it - 1) * m + v] = 1;
			it++, ans--;
		}
		// cout<<nn<<"#"<<mm<<"#"<<q.size()<<"\n";
		while (q.size())
		{
			auto [xx, yy] = q.front();
			q.pop();
			s[yy].erase(xx);
			for (int i = 0; i < 4; i++)
			{
				int t = xx + dx[i];
				int z = yy + dy[i];
				if (t >= u && t <= nn && z >= v && z <= mm && st[(t - 1) * m + z] == 0)
				{
					st[(t - 1) * m + z] = 1;
					q.push({t, z});
					ans--;
				}
			}
		}
}
void solve()
{
	cin >> n >> m >> h >> w >> Q;
	ans = n * m;
	for (int i = 1; i <= n+1; i++)
	{
		for (int j = 1; j <= m; j++)
			s[j].insert(i);
	}
	
	while (Q--)
	{
		int x, y;
		cin >> x >> y;
		bfs(x, y);
		cout << ans << "\n";
	}
}