8-12 队伍训练2

序号题目SolvedAttempted类型补题
ATokitsukaze and Rescue/Sstee1XDDFS+DijkstraSstee1XD、lllllan
BBlow up the EnemySstee1XD/思维lllllan
CContest of Rope Pulling//
DDeliver the Cake/lllllan拆点建图+Dijkstralllllan
EEqual SentencesSstee1XD/基础DPlllllan
FGo Running//
GKindergarten Physicslllllan/输入输出
HLast Problem/Sstee1XD
ITetrahedron//
JPaperfolding//


A - Tokitsukaze and Rescue【最短路+dfs删边】

题意: 给你一张所有点互相直接连通的图,可以炸掉k条边,问你最长的最短路径长度。
题解: 由于所有点都是直接连通的,直接用二维数组存就行了,训练时用链式一直T。方法是dfs,每层进行一次dij,记录每次路径,然后枚举炸掉的路径。(之前写的时候dij还放在了循环了,下次要引起注意。

Sstee1 XD的代码

#include<bits/stdc++.h>

using namespace std;

#define outtime() cerr<<"User Time = "<<(double)clock()/CLOCKS_PER_SEC<<endl
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define bug printf("bug**")
#define coutlf(x) cout << fixed << setprecision(x)
#define uncoutlf  cout.unsetf(ios::fixed)
#define S_IT set<Node>::iterator
#define endl "\n"
#define fi first
#define se second
#define mp(a,b) make_pair(a,b)

int gcd(int a,int b) { return b?gcd(b,a%b):a;}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pII;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const int maxm = 2507;

template<class T> inline void read(T &x){
    int f=0;x=0;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())f|=(ch=='-');
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    if(f)x=-x;
}

int n, k, dis[60], ans;
int path[10][60];
int maps[100][100];

struct qnode{
    int u, w;
    qnode () {}
    qnode (int u, int w) : u(u), w(w) {}
    bool operator < (const qnode &a) const{
        return w > a.w;
    }
};

void Dijkstra(int s, int d) {
    memset(dis, 0x3f, sizeof(dis));
    priority_queue<qnode> q;
    q.push(qnode(s, 0));
    dis[s] = 0;
    path[d][1] = -1;
    while (!q.empty()) {
        int u = q.top().u;
        q.pop();
        for (int i = 1; i <= n; ++i) {
            int w = maps[u][i];
            if (dis[u] + w < dis[i]) {
                dis[i] = dis[u] + w;
                q.push(qnode(i, dis[i]));
                path[d][i] = u;
            }
        }
    }
}

void dfs(int d) {
    Dijkstra(1, d);
    if (d == k) {
        ans = max(ans, dis[n]);
        return;
    }
    for (int i = n; path[d][i] != -1; i = path[d][i]) {
        int v = path[d][i];
        int tmp = maps[i][v];
        maps[i][v] = maps[v][i] = inf;
        dfs(d + 1);
        maps[i][v] = maps[v][i] = tmp;
    }
    
}
void solve() {
    read(n), read(k);
    memset(maps, 0x3f, sizeof(maps));
    int m = (n * (n - 1)) >> 1;
    for (int i = 1, u, v, w; i <= m; ++i) {
        read(u), read(v), read(w);
        maps[u][v] = maps[v][u] = w;
    }
    ans = 0;
    dfs(0);
    printf("%d\n", ans);
}

int _T;

int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
#endif
    read(_T);
    while (_T--) solve();
    return 0;
}

lllllan的代码

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 60;

int T, n, m, k, ans;
int dis[N], pre[N][N], mp[N][N];
struct node{	
	int u, w;
	bool operator <(const node a)const {return a.w < w;}
};
void dijkstra(int cnt){
	memset(dis, inf, sizeof dis);
	priority_queue<node> q;
	q.push({1, 0}); dis[1] = 0;
	while(!q.empty()){
		int u = q.top().u; q.pop();
		for(int i = 1; i <= n; i++){
			int w = mp[u][i];
			if(dis[i] > dis[u] + w){
				dis[i] = dis[u] + w;
				q.push({i, dis[i]});
				pre[cnt][i] = u;
			}
		}
	}
}
void dfs(int u, int cnt){
	dijkstra(cnt);
	if(cnt == k){	ans = max(ans, dis[n]);	return;}
	while(~pre[cnt][u]){
		int v = pre[cnt][u];
		int w = mp[u][v];
		mp[u][v] = mp[v][u] = inf;
		dfs(n, cnt + 1);
		mp[u][v] = mp[v][u] = w;
		u = v;
	}
}
int main(){
	scanf("%d", &T);while(T--){
		memset(mp, inf, sizeof mp);
		memset(pre, -1, sizeof pre);
		scanf("%d%d", &n, &k);
		m = n * (n -1) / 2;while(m--){
			int u, v, w;
			scanf("%d%d%d", &u, &v, &w);
			mp[u][v] = mp[v][u] = w;
		}
		dijkstra(1);	ans = dis[n];
		dfs(n, 0);
		printf("%d\n", ans);
	}
	return 0;
}

B - Blow up the Enemy【思维】

题意: 张三和父亲玩fps,每个人开始有100生命值,可以拿一种武器,每次开枪可以扣对方 A i A_i Ai生命值,每次间隔 D i D_i Di时间开枪,若对手生命值降到0及以下时自己生命值大于0,则获胜,若同时降到0及以下,则各有50%概率获胜。父亲每次随机拿,问张三获胜概率。
题解: 刚开始读错题意,卡了一会儿,后面就发现是个水题。只要把每把武器能获胜的时间记录下来sort一下,遍历一遍就行了。

Sstee1 XD的代码

#include<bits/stdc++.h>

using namespace std;

#define outtime() cerr<<"User Time = "<<(double)clock()/CLOCKS_PER_SEC<<endl
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define bug printf("bug**")
#define coutlf(x) cout << fixed << setprecision(x)
#define uncoutlf  cout.unsetf(ios::fixed)
#define S_IT set<Node>::iterator
#define endl "\n"
#define fi first
#define se second
#define mp(a,b) make_pair(a,b)

int gcd(int a,int b) { return b?gcd(b,a%b):a;}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pII;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const int maxn = 1e3 + 7;
int a[maxn], d[maxn], t[maxn];
void cal(int u) {
	int num = (100 + a[u] - 1) / a[u];
	t[u] = (num - 1) * d[u];
}

int n;

void solve() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d %d", &a[i], &d[i]), cal(i);
	sort(t + 1, t + 1 + n);
	double div = n * 2;
	double head = 0;
	for (int i = 1; i <= n; ++i) {
		if (t[i] == t[1]) head++;
		else {
			head += 2 * (n - i + 1);
			break;
		}
	}
	printf("%.7f\n", head / div);
}

int _T;

int main() {
#ifdef LOCAL
	freopen("input.txt", "r", stdin);
#endif
	scanf("%d", &_T);
	while(_T--) solve();
	return 0;
}

lllllan的代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;

int T, n, num;
int a[N], d[N], t[N];
int main(){
	scanf("%d", &T);while(T--){
		scanf("%d", &n);
		for(int i = 1; i <= n; i++){
			scanf("%d%d", a+ i, d + i);
			num = (100 + a[i] - 1) / a[i];
			t[i] = (num - 1) * d[i];
		}
		int cnt = 0;
		sort(t + 1, t + n +1);
		for(int i = 1; i <= n; i++)
			if(t[i] == t[1])	cnt++;
			else				break;
		printf("%lf\n", (double)(2 * n - cnt) / (2 * n));
	}
	return 0;	
}

D - Deliver the Cake【最短路+拆点】

题意: n个城市m条边,要求手里拿着蛋糕从城市s到达城市t。限制条件为,每条城市有个方向属性:LRM,如果是属性为L,则必须左手拿蛋糕,如果属性M,则左右手都行;可以在任何位置执行换手操作,需要花费时间x。求最短路。
题解: 本来天真地直接在dijkstra的函数里动手脚,琢磨了好久都觉得写的非常完美,:),WA了,无数遍。

WA掉的代码

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int N = 1e6 + 100;

int T, n, m, s, t, cnt;
int vis[N], head[N], hand[N], degree[N];
ll x, dist[N];
struct edge{	int to, next; ll w;}e[N];
struct node{
	int id, hand; ll w;
	node(int a, ll b, int c){	id = a; w = b; hand = c;}
	bool operator <(const node a)const{	return a.w < w;}
};
priority_queue<node>q;
void addedge(int x, int y, ll w){
	e[cnt].to = y;
	e[cnt].w  = w;
	e[cnt].next = head[x];
	head[x] = cnt++;
}
void init(){
	cnt = 0;
	for(int i = 0; i < N; i++){
		dist[i] = -1;
		head[i] = -1;
		vis[i] = vis[i] = hand[i] = 0;
	}
	while(!q.empty())	q.pop();
}
ll dijkstra(){
	dist[s] = 0;
	q.push(node(s, (ll)0, hand[s]));
	while(!q.empty()){
		node p = q.top();
		q.pop();
		if(vis[p.id] == degree[p.id])	continue;
		vis[p.id]++;
		for(int i = head[p.id]; ~i; i = e[i].next){
			edge t = e[i];
			int h = p.hand;ll w = t.w;
			if(hand[t.to] && hand[t.to]!=p.hand){
				if(p.hand)	w += x;
				h = hand[t.to];
			}
			if(vis[t.to] == degree[t.to])	continue;
			if(dist[t.to] == -1 || dist[t.to] > p.w + w){
				dist[t.to] = p.w + w;
				q.push(node(t.to, dist[t.to], h));
			}
		}
	}
	return dist[t];
}
int main(){
	scanf("%d", &T);while(T--){
		init();
		scanf("%d%d%d%d%d", &n, &m, &s, &t, &x);getchar();
		for(int i = 1; i <= n; i++){
			char c;scanf("%c", &c);
			if(c == 'L')	hand[i] = 1;
			if(c == 'R')	hand[i] = 2;
		}
		while(m--){
			int a, b; ll c;
			scanf("%d%d%lld", &a, &b, &c);
			addedge(a, b, c);
			addedge(b, a, c);
			degree[a]++, degree[b]++;
		}
		ll ans = dijkstra();
		printf("%lld\n", ans);
	}
}

拆点建图

  后来补题学习了隔壁队wmh哥哥的思路,dijkstra的函数不变,原模原样地跑。改变存图方式,读入某条边的时候,如果明确两端的城市的方向属性不同,则直接给这条边的权值加上换手时间x。如果其中一段的城市的方向属性为中性的M,则新建一个点,原来的点可以设其方向属性为左,新建的点设其方向属性为右,然后分别建这两条边。说起来很晦涩,直接上图:在这里插入图片描述
  其核心思想就是就是让方向属性中性的城市,一分为二,变成两个带有具体方向属性的城市,然后分别去计算。至于点的编号,则需要作一些小改动,可以把整体的编号乘2,比如编号1的点,其左方向属性的点编号为1 << 1 = 2,其右方向属性的点编号为1 << 1 | 1 = 3。往后所有的点编号同理。最后将起点的两个下属编号与编号0相连,终点的两个下属编号与编号1相连。跑一遍dijkstra的模板求点0到点1的距离。
  采用链式前向星存图,但也要分类讨论,判断某条边两端的方向属性如何,于是···出现又长又丑的代码。我用hand[]数组来记录每个点的方向属性,读入每条边的时候判断两端的方向属性,然后建立新的点,然后存图。光光存图就用了二十几行代码,直接看吐。在这里插入图片描述
  后期的整理归类之后,形成了最后的代码:

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 100;

int T, n, m, s, t, cnt;
int vis[N], head[N];
char hand[N];
ll x, dist[N];
struct edge{	int to, next; ll w;}e[N];
struct node{
	int id; ll w;
	node(int a, ll b){	id = a; w = b;}
	bool operator <(const node a)const{	return a.w < w;}
};
priority_queue<node> q;
void addedge(int x, int y, ll w){ e[cnt].to = y; e[cnt].w  = w; e[cnt].next = head[x]; head[x] = cnt++;}
void doubleadd(int x, int y, ll w){ addedge(x, y, w); addedge(y, x, w);}
void init(){
	cnt = 0;
	for(int i = 0; i < N; i++)	dist[i] = INF, head[i] = -1, vis[i] = 0;
	while(!q.empty())	q.pop();
}
void dijkstra(){
	dist[0] = 0;
	q.push(node(0, 0));
	while(!q.empty()){
		node p = q.top();q.pop();
		if(vis[p.id])	continue;
		vis[p.id] = 1;
		for(int i = head[p.id]; ~i; i = e[i].next){
			edge t = e[i];
			if(!vis[t.to] && dist[t.to] > dist[p.id] + t.w){
				dist[t.to] = dist[p.id] + t.w;
				q.push(node(t.to,dist[t.to]));
			}
		}
	}
	printf("%lld\n", dist[1]);
}
int main(){
	scanf("%d", &T);while(T--){
		init();
		scanf("%d%d%d%d%lld", &n, &m, &s, &t, &x);getchar();
		for(int i = 1; i <= n; i++)	scanf("%c", hand + i);
		while(m--){
			int a, b; ll c;scanf("%d%d%lld", &a, &b, &c);
			if(hand[a] != 'R'){
				if(hand[b] != 'R')	doubleadd(a << 1, b << 1, c);
				if(hand[b] != 'L')	doubleadd(a << 1, b << 1 | 1, c + x);
			}
			if(hand[a] != 'L'){
				if(hand[b] != 'R')	doubleadd(a << 1 | 1, b << 1, c + x);
				if(hand[b] != 'L')	doubleadd(a << 1 | 1, b << 1 | 1, c);
			}
		}
		if(hand[s] != 'R')	addedge(0, s << 1, 0);
		if(hand[s] != 'L')	addedge(0, s << 1 | 1, 0);
		if(hand[t] != 'R')	addedge(t << 1, 1, 0);
		if(hand[t] != 'L')	addedge(t << 1 | 1, 1, 0);
		dijkstra();
	}
	return 0;
}

E - Equal Sentences【动态规划】

题意: 给你一句由英文单词构成的句子,问你最多有几种类似的句子。这里的类似指任意位置上的单词与原句的距离差不超过1,即可以交换相邻的单词,但只能每个单词最多只能交换一次。
题解: 上午写的时候一直没读懂题意,读懂了之后很快就想到了是一个很简单的dp。我们从前往后看,若当前位置的单词与前面的相同,则它们交换与否对答案不造成影响,所以 d p i dp_i dpi = d p i − 1 dp_{i-1} dpi1。如果不相同,则他们可以进行交换,则 d p i dp_i dpi = d p i − 1 dp_{i-1} dpi1 + d p i − 2 dp_{i-2} dpi2

Sstee1 XD的代码

#include<bits/stdc++.h>

using namespace std;

#define outtime() cerr<<"User Time = "<<(double)clock()/CLOCKS_PER_SEC<<endl
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define bug printf("bug**")
#define coutlf(x) cout << fixed << setprecision(x)
#define uncoutlf  cout.unsetf(ios::fixed)
#define S_IT set<Node>::iterator
#define endl "\n"
#define fi first
#define se second
#define mp(a,b) make_pair(a,b)

int gcd(int a,int b) { return b?gcd(b,a%b):a;}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pII;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const int maxn = 1e5 + 7;
const int mod = 1e9 + 7;

int n;
int id[maxn], t, dp[maxn];
string s, pre;
void solve() {
	cin >> n;
	pre = "";
	t = 0;
	dp[0] = dp[1] = 1;
	for (int i = 1; i <= n; ++i) {
		cin >> s;
		if (s == pre) id[i] = t;
		else id[i] = ++t;
		pre = s;
	}
	for (int i = 2; i <= n; ++i) {
		if (id[i] == id[i - 1]) dp[i] = dp[i - 1];
		else dp[i] = (dp[i - 1] + dp[i - 2]) % mod;
	}
	printf("%d\n", dp[n]);
}
int _T;
int main() {
	IOS;
	cin >> _T;
	while (_T--) solve();
	return 0;
}

lllllan的代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;

int T, n;
int dp[N];
string s[N];
int main(){
	scanf("%d", &T);while(T--){
		scanf("%d", &n);
		dp[0] = dp[1] = 1;
		for(int i = 1; i <= n; i++)	cin >> s[i];
		for(int i = 2; i <= n; i++)
			if(s[i] == s[i - 1])	dp[i] = dp[i - 1];
			else	dp[i] = (dp[i - 1] + dp[i - 2]) % mod;
		printf("%d\n", dp[n]);
	}
	return 0;
}

G - Kindergarten Physics【签到】

题意: (其实我是没有看懂的)随便扔两个公式感受一下
  F = G ⋅ m1 · m2 / m2
  G = 6.67430 × 10-11 m3 / (kg ⋅ s2)
题解: 其实不用其实不用,公式里的达到了10-11 恐怖精确度,但是题目的输出只要求10-6 ,所以,所以这题,就是简单的输入输出:)

#include<iostream>
using namespace std;

int main(){
	int t, a, b, c, d;
	scanf("%d", &t);
	while(t--){
		scanf("%d%d%d%d", &a, &b, &c, &d);
		printf("%lf\n", (double)c);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
bugku s1 awd排位赛-12是Bugku安全平台上举办的一场AWD(Attack and Defense)排位赛的第12轮比赛。Bugku是一个致力于网络安全技术研究和交流的平台,这场比赛的目的是为了检验参赛者在攻击和防御方面的技术实力。 在AWD比赛中,参赛队伍被分为进攻方和防守方。进攻方需要利用各种手段,如漏洞利用、渗透测试等,成功攻击防守方的系统或应用程序,获取旗帜(Flag)作为证明。而防守方则需要尽可能地去发现并修补自己系统或应用程序中的漏洞,以防止被攻击方获取旗帜。 在bugku s1 awd排位赛-12中,参赛者们经过激烈的竞争,展示了他们在网络安全领域的知识和技能。比赛中的题目可能涉及到各种不同的技术,如Web安全、二进制安全、密码学等。参赛者需要运用他们的专业知识和创新思维来解决这些挑战。 AWD比赛不仅仅是一场技术竞赛,更是一个学习和提高的机会。通过参与这类比赛,参赛者可以锻炼自己的技术能力,增强他们对网络安全的理解和认识。此外,比赛还促进了参赛队伍之间的交流和合作,提供了一个分享和学习经验的平台。 总而言之,bugku s1 awd排位赛-12是一个让参赛者在攻击和防御中展示自己技术的平台,并提供了一个促进交流和学习的机会。通过这样的比赛,可以推动网络安全领域的发展,培养更多优秀的安全人才。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值