Codeforces Round #154(Div.2)

C. Text Editor

【题意】
有一篇文章,包含 \(i\) 行,每行有 \(a_i\) 个字母,也就是 \(a_i + 1\) 个放置光标的位置。对于一个位于 \((x,y)\) 光标,每次操作可以上移/下移/左移/右移。其中上移和下移比较特殊,如果移动到的那一行没有 \(y\) 个位置,那么会移动到该行最后一个位置。
\(n \le 100, a_i \le 10^5\)
【分析】
一共 \(10^7\) 个点,每个点最多连四条边,边权都为 \(1\),BFS 求最短路即可。卡空间,不要存图,BFS 的时候现场找边即可。\(O(nm)\)

还有一种做法:枚举走到第 \(i\) 行,就知道走到的列号了,然后行数直接就得出来了。可以 \(O(n^2)\),甚至 \(O(n \log n)\)

author: NiloyRafi

#include <bits/stdc++.h>
using namespace std;
int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	int n, a[101], r1, c1, r2, c2;
	cin >> n;
	for (int i=1;i<=n;i++){
		cin>>a[i];
		a[i]++;
	}
	cin>>r1>>c1>>r2>>c2;
	int ans=1e9;
	for (int i=1;i<=n;i++){
		int l=min(i,min(r1,r2));
		int r=max(i,max(r1,r2));
		int c=c1;
		for (int j=l;j<=r;j++)
			c=min(c, a[j]);
		int ans1=abs(i-r1)+abs(i-r2)+abs(c-c2);
		ans=min(ans,ans1);
	}
	cout<<ans;
	return 0;
}
D. Table with Letters - 2

【题意】
给定一个 \(n \times m\) 的小写字母矩阵。求满足以下条件矩形的个数:

  • 四个顶点字母一样。
  • 矩形的 \(a\) 个数 \(\le k\)

\(n, m \le 400\)
【分析】
首先暴力是 \(O(n^4)\),我们优化一个维度就差不多了。

考虑固定 \(x_1, x_2\)。场上首先想到固定 \(x_3\) 然后二分,预处理前缀和。这个的时间复杂度是 \(O(n^3 \log n + n^3 \times 26)\),还是无法通过。

第二场想到具有可二分性,而且 \(a\) 的个数也很好记录,那二分换成双指针是可以优化掉一个 \(\log\) 的。使用双指针即可优化一个维度。双指针注意加入和删除时刻的细节处理,以及 \(l,r\) 初始值的设置,使用闭区间还是开区间。

author: nealzane

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
const int inf = 1e9;
void cmax(int &x, int y) {if(x < y) x = y;}
void cmin(int &x, int y) {if(x > y) x = y;}
char c[410][410];
int ans;
int a[410][410]; 
int s[30];
int v=25;
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);    
    int n, m, z;cin>>n>>m>>z;
    f(i,1,n)f(j,1,m)cin>>c[i][j];
    f(i,1,n)f(j,1,m)
        a[i][j] = a[i-1][j] + a[i][j-1]-a[i-1][j-1]+(c[i][j]=='a');
    f(i, 1, n)f(j,i+1,n){
        f(k,0,v)s[k]=0;
        for(int l=1,r=0;l<=m;l++){
            if(c[i][l]!=c[j][l]){
                continue;
            }
            else {
                while(r<m&&a[j][r+1]-a[j][l-1]-a[i-1][r+1]+a[i-1][l-1]<=z){
                    r++;
                    if(c[i][r] == c[j][r]) {
                        s[c[i][r] - 'a']++;
                    }
                }
                s[c[i][l] - 'a']--;
                if(l<=r)ans += s[c[i][l] - 'a'];
            }
        }
    }
    cout << ans << endl;
    return 0;
}
E. Printer

【题意】
一台打印机每秒可以打印一页纸张。有 \(n\) 个打印任务,每个任务都有送达时间 \(t_i\),页数 \(s_i\) 和优先级 \(p_i\)。优先级之间互相不同。在任意一秒开始时,打印机会选择已经送达并且还没有打印完毕的任务中优先级最大的一个,并且打印该任务的一页。目前存在一个任务 \(x\),你不知道它的优先级,但是你知道该任务在第 \(T\) 时刻打印完毕,你也知道其他所有信息。求 \(x\) 的优先级和每个任务何时打印完毕。
\(1 \le n \le 50000, 0\le t_i \le 10^9, 1 \le s_i, p_i \le 10^9,1\le T\le 10^{15}\)

要求 \(1 \le p_x \le 10^9, p_x \neq p_i\)
【分析】
首先,如果知道 \(p_x\),可以利用 set 在 \(O(n \log n)\) 时间内模拟完所有打印操作并且求出每个任务打印完毕的时间。
那么考虑如何计算 \(p_x\)。我们发现,如果 \(p_x\) 最高,那么它应该在第 \(s_x + t_x\) 时间被打印完。如果不是这样,那么一定是有其他比它优先级更高的在它打印的过程中间抢着打印完了。
考虑实际打印完的时间 \(T\)\(s_x + t_x\) 的差值为 \(k\)。由于只有在 \(x\) 之前开始,但是在 \(x\) 任务开始的时候还没打印完的任务和在 \(x\) 之后,但在 \(x\) 任务结束前开始的任务才会影响打印 \(x\) 的进程。并且打印完 \(x\) 之前必然完整打印完所有优先级比 \(x\) 更高的任务。对于上述范围内的任务排序,并且从高到低遍历,打印完某个任务需要花费的时间加起来等于 \(k\) 的话,\(x\) 的优先级就恰好是在该任务的下面。
这样,总时间复杂度是 \(O(n \log n)\)

需要注意的是,有些任务虽然不影响 \(x\) 任务的执行但是会妨碍 \(x\) 优先级的放置。因此预处理每一个优先级往下第一个可以放置的是多少。但是数据全是随机的,根本没考虑这一点,把直接 \(-1\) 的做法也放过去了。

还有一个做法比较简单,直接暴力二分 \(x\) 的优先级然后模拟,是 \(O(n \log^2 n)\) 的。

author: rng_58

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>

using namespace std;

#define REP(i,n) for((i)=0;(i)<(int)(n);(i)++)
#define foreach(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)

typedef long long ll;

int N,id;
int t[50010],s[50010],p[50010];
vector <int> p2,v;

vector <pair <int, int> > queries; // t, id
int page[50010];
priority_queue <pair <int, int> > q; // p, id
ll ans[50010];

void simulate(void){
    int i;
    
    REP(i,N) page[i] = s[i];
    
    ll T = 0;
    int next = 0;
    
    while(1){
        while(next < N && queries[next].first == T){
            int x = queries[next].second;
            q.push(make_pair(p[x], x));
            next++;
        }
        
        if(q.empty()){
            if(next == N) return;
            T = queries[next].first;
            continue;
        }
        
        int y = q.top().second;
        ll T2 = T + page[y];
        if(next < N) T2 = min(T2, (ll)queries[next].first);
        
        page[y] -= T2 - T;
        if(page[y] == 0){
            q.pop();
            ans[y] = T2;
        }
        
        T = T2;
    }
}

int main(void){
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    
    int i;
    ll T;
    
    cin >> N;
    REP(i,N) scanf("%d%d%d", &t[i], &s[i], &p[i]);
    cin >> T;
    REP(i,N) if(p[i] == -1) id = i;
    
    p2.push_back(0);
    p2.push_back(1000000001);
    REP(i,N) if(p[i] != -1) p2.push_back(p[i]);
    sort(p2.begin(),p2.end());
    REP(i,p2.size()-1) if(p2[i+1] - p2[i] >= 2) v.push_back((p2[i+1] + p2[i]) / 2);
    
    REP(i,N) queries.push_back(make_pair(t[i],i));
    sort(queries.begin(),queries.end());
    
    int low = 0, high = v.size();
    while(high-low > 1){
        int mid = (high + low) / 2;
        p[id] = v[mid];
        simulate();
        if(ans[id] >= T) low = mid; else high = mid;
        if(ans[id] == T) break;
    }
    
    cout << v[low] << endl;
    REP(i,N){
        cout << ans[i];
        if(i == N-1) cout << endl; else cout << ' ';
    }
    
    return 0;
}
posted @ 2022-11-30 23:23  OIer某罗  阅读(29)  评论(0编辑  收藏  举报