GRYZ10.18模拟赛解题报告

写在前面

期望得分:\(100+100+100=300pts\)
实际得分:\(100+100+10=210pts\)

挂了一个 \(0\) /cy

理想很丰满,现实很骨感。

题挺没意思的,第一题进制,第二题二分+最短路,第三题原题,也是二分

下面来介绍一下我的做题过程:

14:00 开始。
14:15 读完题,感觉今天的题都有点奇怪。
14:15 写完 T1 并自己写了一个 checker 对拍。
15:00 写完 T2,然后发现 T3 是原题。
15:22 写完 T3,然后一直划水。
中间又造了几个极弱的样例,感觉 T2,T3 都没有问题。
17:30 考试结束,T3 挂了 90,原题啊原题!我挂了 90!我是不是菜?啊 我 是 不 是 菜 啊?

T1

你发现能填的数是 \(3^0,3^1,3^2...\),然后这个东西转化成三进制刚好对应着三进制的每一位。

因为只能放一次嘛,你在想如果你把 \(W\) 转化成三进制后是 \(11010011...\) 的形式(只有 \(1\)\(0\)

你考虑把要处理的 \(W\) 来转化成你想要的形式,怎么办?加砝码!

假设 \(W\) 拆成三进制后是 \(112001212022...\) 之类的,那么你从低位向高位遍历,如果第 \(x\) 为是 \(2\) 就加一个 \(3^{x-1}\) 的砝码,然后进位。如果是 \(1\)\(0\) 就不用管,遍历完就能得到我们想要的那个形式了。就可以直接得到左边要加的砝码。

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const LL MAXN = 1e5+5;
const LL INF = 1e9+7;
const LL mod = 1e9+7;

LL W;
LL stc[200], sc = 0;
LL a[200], top1 = 0;
LL b[200], top2 = 0;

LL read(){
    LL s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

int main()
{
    freopen("entertain.in","r",stdin);
    freopen("entertain.out","w",stdout);
    b[1] = 1;
    for(LL i = 2; i <= 35; ++i) b[i] = b[i - 1] * 3;
	W = read();
	LL x = W;
	while(x) {
	    stc[++sc] = x % 3;
	    x /= 3;
    }
//    for(int i = 1; i <= sc; ++i) cout<<stc[i]<<" "; puts("");
    for(LL i = 1; i <= sc; ++i) {
        stc[i + 1] += stc[i] / 3;
        stc[i] %= 3;
        if(stc[i] == 2) {
            a[i] = true;
            stc[i] = 0, stc[i + 1] ++;
        }
        if(stc[i + 1] > 0) sc = max(sc, i + 1);
    }
//    for(int i = 1; i <= sc; ++i) cout<<stc[i]<<" "; puts("");
//    for(int i = 1; i <= sc; ++i) cout<<a[i]<<" "; puts("");
//    int cnt1 = 0, cnt2 = 0;
//    for(int i = 1; i <= sc; ++i) cnt1 += (stc[i] == 1), cnt2 += a[i];
//    cout<<cnt1<<" "<<cnt2 + 1<<"\n"; 
    for(LL i = 1; i <= sc; ++i) {
        if(stc[i] == 1) {
            cout<<b[i]<<" ";
        }
    }
    puts("");
    cout<<W<<" ";
    for(LL i = 1; i <= sc; ++i) {
        if(a[i]) {
            cout<<b[i]<<" ";
        }
    }
    puts("");
    return 0;
}

T2

分层图

这个题没给 \(k\) 的数据范围,\(k\) 大了应该能卡掉分层图做法。

每用几次免消耗体力的机会就是从上一层走到下一层。

然后就可以建 \(k\) 层图,跑 Dij 和 SPFA 都行,记录最大值即可。

二分答案

这个应该是正解。

二分答案 \(x\),把 \(w > x\) 的边的权值看做 \(1\)\(w \le x\) 的边的权值看做 \(0\),然后跑最短路。

如果 \(dis_n \le k\) 说明这个答案 \(x\) 合法,然后下调边界,否则表示不合法,上调边界。

代码是二分答案的做法:

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct edge {
    int to, w, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge = 1;

int n, m, K;
int dis[MAXN];
bool vis[MAXN];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

void add_edge(int from, int to, int w) { e[++num_edge] = (edge){to, w, head[from]}, head[from] = num_edge; }

bool SPFA(int lim) {
    queue<int> q;
    memset(dis, 0x3f, sizeof dis);
    dis[1] = 0, vis[1] = true, q.push(1);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        vis[u] = false;
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if(dis[v] > dis[u] + (e[i].w > lim)) {
                dis[v] = dis[u] + (e[i].w > lim);
                if(!vis[v]) q.push(v), vis[v] = true;
            } 
        }
    }
    return dis[n] <= K;
}

int main()
{
    freopen("novel.in","r",stdin);
    freopen("novel.out","w",stdout);
	n = read(), m = read(), K = read();
	int l = 0, r = 1000000, ans = 0;
	for(int i = 1, u, v, w; i <= m; ++i) {
        u = read(), v = read(), w = read();
        add_edge(u, v, w), add_edge(v, u, w);
    }
    SPFA(r);
    if(dis[n] == dis[0]) {
        puts("-1");
        return 0;
    }
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(SPFA(mid)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    printf("%d\n", ans);
    return 0;
}

T3

二分惩罚值,好像也叫什么 wqs二分,洛谷原题

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e5+10;
const int INF = 1;
const int mod = 1;

struct edge{
	int from, to, w, hb;
}e[MAXN];
int num_edge = 0;

int n, m, b;
int ans = 0, cnt = 0, cnt0 = 0;
int fa[MAXN];

int read(){
	int s = 0, f = 0;
	char ch = getchar();
	while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
	return f ? -s : s;
}

bool cmp(edge x, edge y){ return x.w < y.w; }

void add_edge(int from, int to, int w, int hb){ e[++num_edge] = (edge){from, to, w, hb}; }

int find(int x){ return fa[x] == x ? x : fa[x] = find(fa[x]); }
	
void kruskal(){
	for(int i = 0; i <= n; ++i) fa[i] = i;
	for(int i = 1; i <= num_edge; ++i){
		int uf = find(e[i].from), vf = find(e[i].to);
		if(uf != vf){
			fa[uf] = vf;
			ans += e[i].w;
			cnt++;
			if(e[i].hb == 0) cnt0++;
			if(cnt == n - 1) return ;
		}
	}
}

bool check(int mid){
	for(int i = 1; i <= m; ++i) if(e[i].hb == 0) e[i].w -= mid;
	cnt = 0, cnt0 = 0, ans = 0;
	sort(e + 1, e + m + 1, cmp);
	kruskal();
	for(int i = 1; i <= m; ++i) if(e[i].hb == 0) e[i].w += mid;
	return cnt0 >= b;
}

int main()
{
	n = read(), m = read(), b = read();
	int u, v, w, hb;
	for(int i = 1; i <= m; ++i){
		u = read() + 1, v = read() + 1, w = read(), hb = read();
		add_edge(u, v, w, hb);
	}
	int l = -105, r = 105, last;
	while(l <= r){
		int mid = (l + r) >> 1;
		if(check(mid)) {
			r = mid - 1, last = mid;
		}
		else l = mid + 1; 
	}
//	last -= 1;
//	cout<<check(last)<<endl;
	ans = 0, cnt = 0, cnt0 = 0;
	check(last);
	printf("%d", ans + last * b);
	return 0;
}
posted @ 2021-10-18 21:59  Suzt_ilymtics  阅读(60)  评论(2编辑  收藏  举报