2021 CCPC 桂林 ADEGIK

2021 CCPC 桂林 ADEGIK

https://codeforces.com/gym/103409
女队vp。就做了四道比较签到的题,后续补了两题,感觉比较考察思维。本身的代码不难写。其中,D题要能明白那个贪心的思想,尽量把大的放在前面,并且要知道怎么才能把大的放在前面!!!K题非常神奇,想了半天但其实是直接在边权为1的最短路的基础上dfs每一条路求最小值。

A Hero Named Magnus (签到)

签到,记得开long long

#include <bits/stdc++.h>
#define ll long long

using namespace std;

void solve () {
    ll n;
    cin >> n;
    cout << 2ll * n - 1 << endl;
}

int main () {
    int t;
    cin >> t;
    while (t--) solve ();
}

D. Assumption is All You Need (贪心)

题解思路:

我的丑丑示意图:

其实就是贪心思想,想要把大的数尽可能留在前面,所以就要按照左边吧的策略来交换,这样大的数最多会后移一位,不会对后面的交换造成更大影响。否则直接把大的一步换过去只会更劣。

个人觉得还是思维量不够,其实放cf上并不是一个多难的题,还是要多训!!

(这题cincout会超时)

#include <bits/stdc++.h>
#define ll long long

using namespace std;
typedef pair<int, int> pii;
const int N = 2025;
int n, a[N], b[N];

void solve () {
    vector<pii> v;
	scanf ("%d", &n);
    for (int i = 1; i <= n; i++)    scanf ("%d", &a[i]);
    for (int i = 1; i <= n; i++)    scanf ("%d", &b[i]);
    for (int i = 1; i <= n; i++) {
        if (a[i] == b[i])   continue;
        if (a[i] < b[i]) {
            printf ("-1\n");
            return ;
        }
        while (a[i] != b[i]) {
            for (int j = i + 1; j <= n; j++) {
                if (a[j] < a[i] && a[j] >= b[i]) {
                    swap (a[i], a[j]);
                    v.push_back ({i, j});
                }
            }
        }
    }
    printf ("%d\n", v.size ());
    for (auto i : v)    printf ("%d %d\n", i.first, i.second);
}

signed main() {
    int t;
    scanf ("%d", &t);
    while (t--)	solve ();      
}

E. Buy and Delete (求最小环)

如果一条边也选不了就0步。
否则就是找最小的环,如果能构造出来就两步,否则一步
求最小环的方法,每个点出发求一遍最短路,然后在回到起点的时候记录环的长度,综合求一个最小值。

#include <bits/stdc++.h>
#define ll long long

using namespace std;
typedef pair<int, int> pii;
const int N = 2005, M = 5005;
int h[N], e[M], ne[M], w[M], idx;
int dis[N], n, m, k, minn = 1e6;
bool vis[N];

void add (int a, int b, int c) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

int dijkstra(int st) {
    memset (dis, 0x3f, sizeof dis);
    memset (vis, false, sizeof vis);
    dis[1] = 0;
    priority_queue<pii, vector<pii>, greater<pii>>q;
    q.push({0, st});//dis num

    int sum = 1e9 + 5;
    while (!q.empty()){
        auto t = q.top();
        q.pop();
        int num = t.second, distance = t.first;
        if (vis[num])    continue;//pass
        vis[num] = true;

        for (int i = h[num]; i != -1; i = ne[i]){
            int j = e[i];
            if (j == st)    sum = min (sum, distance + w[i]);
            if (dis[j] > distance + w[i]){
                dis[j] = distance + w[i];
                q.push({dis[j], j});
            }
        }
    }
    return sum;
}

int main () {
    memset (h, -1, sizeof h);
    scanf ("%d%d%d", &n, &m, &k);
    while (m--) {
        int a, b, c;
        scanf ("%d%d%d", &a, &b, &c);
        add (a, b, c);
        minn = min (minn, c);
    }
    if (minn > k) {
        printf ("0");
        return 0;
    }
    int sum = 1e9 + 5;
    for (int i = 1; i <= n; i++) {
        //if (vis[i]) continue;
        sum = min (sum, dijkstra (i));
    }
    //cout << sum << endl;
    if (sum <= k)   printf ("2");
    else    printf ("1");
}

G. Occupy the Cities (二分)

这是我想的,二分最少时间

check的方法就是:假设当前要 check \(x\),用变量 \(l\) 来记录碰到 \(1\) 之前的连续 \(0\) 个数,如果再碰到 \(1\) 之前 \(l > x\),则 return false

在遇到 \(1\) 时又分为两种情况,一种是之前已经有一个 \(1\) (能影响左边的 \(l\)\(0\) 了),那么可以把 \(l\) 清空。若之前没有 \(1\),那么表示之前是 \(0...01\) 的形式,标记一下已经遇到了一个 \(1\),这里用 \(find=1\) 表示遇到了 \(1\)。接着往后统计碰到 \(1\) 之后的连续 \(0\) 的数量 \(r\)
\(r==x-1\) 时:若 \(l==x\),这一段 \(1\) 就需要 \(x\) 天才能把左右的 \(0\) 给覆盖,从这开始断开,接着统计下一段
\(r==x\) 时:若 \(l<x\) 这一段 \(1\) 就需要 \(x\) 天才能把左右的 \(0\) 给覆盖,从这开始断开,接着统计下一段;否则 \(l==x\),那么 这个 \(1\) 需要 \(x+1\) 天才能把左右长都为 \(x\)\(0\) 给覆盖掉(其实这个会在 \(r==x-1\) 的情况中给判掉)

就按照上述去 \(check\),如果最后还剩下 \(0\) 没消掉,那么就是不合法的。注意中间变量置 \(0\) 的小细节。

#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
int n;
string s;

bool check (int x) {
	//cout << x << endl;
	int l = 0, r = 0, find = 0;
	for (int i = 1; i <= n; i++) {
		if (s[i] == '0') {
			if (find)	r++;
			else	l++;
		}
		else {
			if (find)	l = r = 0;
			else	find = 1;
		}
		if (l > x)	return false;
		
		if (find && r == x - 1) {
			if (l == x)	l = r = find = 0;
		}
		if (find && r == x) {
			if (l < x)	l = r = find = 0;
			else	return false;
		}
		//cout << l << ' ' << r <<' ' <<find <<endl;
	}
	if (l && !find)	return false;
	return true;
}

void solve () {
	cin >> n >> s;
	int cnt = 0;
	for (int i = 0; i <n; i++)	cnt += (s[i] == '0');
	if (cnt == 0) {
		cout << "0\n";
		return ;
	}
	s = ' ' + s;
	int l = 1, r = n;
	while (l < r) {
		int mid = l + r >> 1;
		if (check (mid))	r = mid;
		else	l = mid + 1;
	}
	cout << r << endl;
}

signed main() {
   ios::sync_with_stdio(false);
   cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--) {
    	solve (); 
	} 
     
}
/*
5
15
010000010000010
8
10000100
9
100001000
10
1000010000
11
00010001000
*/

I. PTSD (贪心)

小贪心。从后往前用0,1来抵,如果当前数后面有多余的0,1就可选,否则选不上。

#include <bits/stdc++.h>
#define ll long long

using namespace std;
int n;
string s;

void solve () {
    cin >> n >> s;
    s = ' ' + s;
    ll c1 = 0, c0 = 0, ans = 0;
    for (int i = n; i >= 1; i--) {
        if (s[i] == '0') c0++;
        else {
            if (c0) ans += i, c0--;
            else if (c1)    ans += i, c1--;
            else    c1++;
        }
    }
    cout << ans << endl;
}

int main () {
    int t;
    cin >> t;
    while (t--) solve ();
}

K. Tax (bfs + dfs)

神秘题,本质就是大暴力,在边权为1的图上跑最短路,这样保证了不会绕路(走重复的某条边),即走这样的路一定是最优的,然后在最短路的基础上dfs每一条边算最短路即可,复杂度不会算,总之很神奇。

#include <bits/stdc++.h>
#define int long long

using namespace std;
typedef pair<int, int> PII;
const int N = 55, M = N * N;
int h[N], e[M], ne[M], w[M], idx;
int n, m, v[M], dis[N], ans[N], cnt[M];
bool vis[N];

void add (int a, int b, int c) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

void dijkstra(){
    memset (dis, 0x3f, sizeof dis);
    dis[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>>q;
    q.push({0, 1});//dis num

    while (!q.empty()){
        auto t = q.top();
        q.pop();
        int num = t.second, distance = t.first;
        if (vis[num])    continue;//pass
        vis[num] = true;

        for (int i = h[num]; ~i; i = ne[i]){
            int j = e[i];
            if (dis[j] > distance + 1){ //视作边权为1
                dis[j] = distance + 1;
                q.push({dis[j], j});
            }
        }
    }
}

void dfs (int u, int sum) {
    ans[u] = min (ans[u], sum);
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (dis[j] == dis[u] + 1) {
            cnt[w[i]]++;
            dfs (j, sum + cnt[w[i]] * v[w[i]]);
            cnt[w[i]]--;
        }
    }
}

signed main () {
    memset (h, -1, sizeof h);
    scanf ("%lld%lld", &n, &m);
    for (int i = 1; i <= m; i++)    scanf ("%lld", &v[i]);
    for (int i = 1; i <= m; i++) {
        int a, b, c;
        scanf ("%lld%lld%lld", &a, &b, &c);
        add (a, b, c), add (b, a, c);
    }
    dijkstra ();
    //for (int i = 2; i <= n; i++)    printf ("%d\n", dis[i]);
    memset (ans, 0x3f, sizeof ans);
    dfs (1, 0);
    for (int i = 2; i <= n; i++)    printf ("%lld\n", ans[i]);
}

B题set维护不太会写,J后缀树也不会www

posted @ 2023-10-10 20:44  Sakana~  阅读(15)  评论(0编辑  收藏  举报