7.30 2020 Multi-University Training Contest 4題解及補題
目錄
7.30 2020 Multi-University Training Contest 4題解及補題
比賽過程
開局1011找到規律還是比較順利的,1002殺掉100血,情況欠考慮,1005注意分好情況
題解
1002 Blow up the Enemy
題意
有對父子在玩槍戰遊戲,有 n 把槍,,每把槍都有兩個屬性 A :攻擊力 ,D:冷卻時間。
初始都有 100 滴血,父親會隨機從 n 把槍裡選擇一把槍。
關於比賽有如下規定:
- 他倆第一槍一定是同時開的
- 能開槍就開槍
- 如果兩人同時死亡,各有 50 % 的概率獲勝
現在要你為兒子選擇一把槍,使得其勝利的概率最大,輸出這個概率
解法
直接判斷殺掉 100 滴血最快的槍有幾把,輸出 \(1.0−x/2n\)。
程式碼
#include <algorithm> #include <bitset> #include <cctype> #include <cerrno> #include <clocale> #include <cmath> #include <complex> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <deque> #include <exception> #include <fstream> #include <functional> #include <limits> #include <list> #include <map> #include <iomanip> #include <ios> #include <iosfwd> #include <iostream> #include <istream> #include <ostream> #include <queue> #include <set> #include <sstream> #include <stack> #include <stdexcept> #include <streambuf> #include <string> #include <utility> #include <vector> #include <cwchar> #include <cwctype> using namespace std; #define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) const int inf = 0x3f3f3f3f; typedef long long ll; const int maxn = 100500; const ll mod = 1e9 + 7; typedef pair<int,int> pii; const double pi = acos(-1); struct node { int x,y; // double c; int c; }a[2005]; bool cmp(node a,node b) { return a.c<b.c; } int main() { IO; int T,n; cin>>T; while(T--) { cin>>n; bool flag=false; int cnt=0; for(int i=0;i<n;i++) { cin>>a[i].x>>a[i].y; int cn=100-a[i].x; if(a[i].x>=100) {flag=true; cnt++;} // a[i].c=double(a[i].x*1.0/a[i].y); if(cn>0) { if(cn%a[i].x!=0) { a[i].c=a[i].y*(cn/a[i].x+1); } else { a[i].c=a[i].y*(cn/a[i].x); } } } double ans=0.0; if(flag==false) { int tem=1; sort(a,a+n,cmp); int te=a[0].c; for(int i=1;i<n;i++) { if(a[i].c==te) { // cout<<a[i].c<<" "<<te<<endl; tem++; } else break; } ans=1.0-(0.5*tem*1.0/(n*1.0)); } else { ans=1.0-(0.5*cnt*1.0/(n*1.0)); } cout<<fixed<<setprecision(6)<<ans<<endl; } return 0; }
1003 Contest of Rope Pulling
題意
T 組資料,有n,m代表每個班級的人數,每個人有兩種屬性wi(力量值),vi(魅力值),
問你從兩班中選擇兩個子集(可為空),使得兩個子集的力量值和相等,求選出來的所有人的魅力值和的最大值
解法
可以將兩班人和為一個集合,需要將第二班的力量改為負數。之後進行dp,那麼dp[0]就是答案,由於下標可能是負數所以需要加一個數表示0
需要注意的是初始化為-INF,其次就是注意一下不要超過陣列的範圍。
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll N = 3e3 + 7; const ll M = 1e5 + 7; const ll INF = 1e18; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int t; int n, m; struct node{ ll w, v; }; node a[N]; ll dp[M]; ll getdp(int n) { fill(dp, dp + M, -INF); dp[50000] = 0; for (int i = 1; i <= n;i++) { if(a[i].w >= 0) { for (int j = 1e5; j >= a[i].w;j--) { if(dp[j - a[i].w] != -INF) dp[j] = max(dp[j], dp[j - a[i].w] + a[i].v); } } else { int lim = 1e5 + a[i].w; for (int j = 0; j <= lim; j++) { if (dp[j - a[i].w] != -INF) dp[j] = max(dp[j], dp[j - a[i].w] + a[i].v); } } } return dp[50000]; } int main() { srand(time(0)); t = read(); while(t--) { n = read(), m = read(); for (int i = 1; i <= n + m;i++) { a[i].w = read(), a[i].v = read(); if(i > n) a[i].w = -a[i].w; } random_shuffle(a + 1, a + n + m + 1); ll ans = 0; ans = getdp(n + m); printf("%lld\n", ans); } return 0; }
1004 Deliver the Cake
題意
給定一張無向圖,求從起點到終點的最短路徑。其中,每個點有一定限制,規定在該點時必須是以下三種狀態之一:L,R,M
經過L點時必須左手拿蛋糕,經過R點時必須右手拿蛋糕,經過M點時左手右手都可以。
在路徑中可以進行任意次換手,每次換手花費 x 。
解法
拆點+dij。這個題還是比較有意思的,我們也學習了拆點的技巧,將必須左手的村莊拆成兩個左手,右手拆成兩個右手,M的村莊拆成一左一右,然後跑dijsktra,然後因為是雙向邊,拆點後點數量變為原來2倍,連邊情況變為原來的4倍,所以存邊的陣列要開8倍;
程式碼
#include<bits/stdc++.h>
using namespace std;
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define repp(i,a,b) for(int i=a;i<b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define perr(i,a,b) for(int i=a;i>b;i--)
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const ll mod=998244353;
const int maxn=200010;
char ss[maxn],sss[maxn];
int n,m,s,t,x;
ll dis[maxn];
int head[maxn],tot;
struct node
{
int u,v,nxt;
ll w;
}e[8*maxn];
struct qnode
{
int v;
ll dis;
bool operator < (const qnode &r)const
{
return dis>r.dis;
}
};
void add(int u,int v,ll w)
{
e[tot].u=u;
e[tot].v=v;
e[tot].w=w;
e[tot].nxt=head[u];
head[u]=tot++;
e[tot].u=v;
e[tot].v=u;
e[tot].w=w;
e[tot].nxt=head[v];
head[v]=tot++;
}
void init()
{
rep(i,1,2*n)head[i]=-1;
tot=0;
}
ll dij(int s)
{
rep(i,1,2*n)dis[i]=LINF;
priority_queue<qnode>q;
dis[s]=0;
q.push({s,0});
qnode tmp;
while(!q.empty())
{
tmp=q.top();
q.pop();
int u=tmp.v;
if(tmp.dis>dis[u])continue;
for(int i=head[u];i!=-1;i=e[i].nxt)
{
int v=e[i].v;
ll w=e[i].w;
ll z=(sss[u]==sss[v]?0:x);
if(dis[u]+w+z<dis[v])
{
dis[v]=dis[u]+w+z;
q.push({v,dis[v]});
}
}
}
return min(dis[t],dis[t+n]);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d%d",&n,&m,&s,&t,&x);
init();
scanf("%s",ss);
rep(i,1,n)
{
if(ss[i-1]=='M')sss[i]='L',sss[i+n]='R';
else if(ss[i-1]=='L')sss[i]=sss[i+n]='L';
else sss[i]=sss[i+n]='R';
}
while(m--)
{
int u,v;
ll w;
scanf("%d%d%lld",&u,&v,&w);
add(u,v,w);add(u+n,v,w);
add(u+n,v+n,w);add(u,v+n,w);
}
printf("%lld\n",min(dij(s),dij(s+n)));
}
return 0;
}
1005 Equal Sentences
題意
對句子S中的單詞向前移一位或向後移一位或不移得到的兩個句子幾乎相等,求多少不同的句子,幾乎等於S,包括S本身
解法
程式碼
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
int main(){
ll t;
scanf("%lld",&t);
while(t--){
ll n;
scanf("%lld",&n);
string s[n];
ll a[n],c[n];
for(ll i=0;i<n;i++){
cin>>s[i];
a[i]=0;
c[i]=0;
}
if(n==1){
cout<<1<<endl;
continue;
}
ll ans=1;
if(s[n-1]!=s[n-2]){
c[n-2]=2;
}
else{
c[n-2]=1;
}
if(n-2==0){
cout<<c[0]<<endl;
continue;
}
if(s[n-3]!=s[n-2]){
c[n-3]=c[n-2]+1;
}
else{
c[n-3]=c[n-2];
}
if(n-3==0){
cout<<c[0]<<endl;
continue;
}
for(ll i=n-4;i>=0;i--){
if(s[i]!=s[i+1]){
c[i]=(c[i+2]+c[i+1])%mod;
}
else{
c[i]=c[i+1];
}
}
cout<<c[0]<<endl;
}
return 0;
}
1007 Go Running
題意
有一條路可以看成是無盡頭的數軸,學生可以選擇一個點開始跑步,可以選擇從任意時間t1開始跑,從任意時間t2結束跑步,也可以選擇跑步方向,但跑步速度恆定為1 m/s
跑步開始前不會出現在數軸上,跑步結束後也不會出現在數軸上
這條路上有一些監控,你收到了n份報告,每份報告有兩個資料xi和ti,表示時間為ti秒時在數軸的xi位置有至少一個學生在跑步,問最少有多少個學生在跑步
解法
二分圖最大匹配問題
假設報告時刻為t,位置為x,那麼相同的t+x或者t−x能夠合併成一個人。可以用map記錄序號,將t+x和t−x相連,再用HK演算法求一下最大匹配就好了。
程式碼
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 5e5 + 10;
const int INF = 0x3f3f3f3f;
vector<int> G[maxn];
int uN;
int Mx[maxn], My[maxn];
int dx[maxn], dy[maxn];
int dis;
bool used[maxn];
bool SearchP() {
queue<int>Q;
dis = INF;
memset(dx, -1, sizeof(dx));
memset(dy, -1, sizeof(dy));
for (int i = 1; i <= uN; i++)
if (Mx[i] == -1) {
Q.push(i);
dx[i] = 0;
}
while (!Q.empty()) {
int u = Q.front();
Q.pop();
if (dx[u] > dis)break;
int sz = G[u].size();
for (int i = 0; i < sz; i++) {
int v = G[u][i];
if (dy[v] == -1) {
dy[v] = dx[u] + 1;
if (My[v] == -1)dis = dy[v];
else {
dx[My[v]] = dy[v] + 1;
Q.push(My[v]);
}
}
}
}
return dis != INF;
}
bool DFS(int u) {
int sz = G[u].size();
for (int i = 0; i < sz; i++) {
int v = G[u][i];
if (!used[v] && dy[v] == dx[u] + 1) {
used[v] = true;
if (My[v] != -1 && dy[v] == dis)continue;
if (My[v] == -1 || DFS(My[v])) {
My[v] = u;
Mx[u] = v;
return true;
}
}
}
return false;
}
int MaxMatch() {
int res = 0;
memset(Mx, -1, sizeof(Mx));
memset(My, -1, sizeof(My));
while (SearchP()) {
memset(used, false, sizeof(used));
for (int i = 1; i <= uN; i++)
if (Mx[i] == -1 && DFS(i))
res++;
}
return res;
}
map<int, int> m1, m2;
int t, n, tt, xx;
int main() {
scanf("%d", &t);
while (t--) {
memset(G, 0, sizeof(G));
m1.clear();
m2.clear();
scanf("%d", &n);
int u = 0, v = 0;
for (int i = 1; i <= n; i++) {
scanf("%d%d", &tt, &xx);
int x = tt + xx;
int y = tt - xx;
if (!m1[x]) m1[x] = ++u;
if (!m2[y]) m2[y] = ++v;
G[m1[x]].push_back(m2[y]);
}
uN = u;
printf("%d\n", MaxMatch());
}
return 0;
}
1011 Kindergarten Physics
題意
質量a,b的物體,初始距離d米,c秒後的距離
解法
水題,直接輸出d或者,可以魔鬼-0.0000000001,小於精確度的
程式碼
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--){
double a,b,c,d;
scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
c-=0.0000000001;
printf("%.10lf\n",c);
}
}
1012 Last Problem
題意
給出一個無限大的二維平面,需要在平面內進行染色,每次可以選擇一個點 ( x , y ) 將其染色為 n 的前提是,相鄰四個格子必須分別已經染了 n - 1 , n - 2 , n - 3 , n - 4 四種顏色,染色可以覆蓋,構造出一種合理的染色方案,使得可以在平面上出現顏色 n
解法
構造題,對於某一時刻
n-1
n-2 n n-3
n-4
程式碼
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
#include <cwchar>
#include <cwctype>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e3+100;
const int b[5][2]={0,0,0,-1,-1,0,1,0,0,1};
int maze[N][N];
int cnt=0;
void dfs(int x,int y,int val)
{
if(val<=0)
return;
for(int i=1;i<=4;i++)
{
int xx=x+b[i][0],yy=y+b[i][1];
if(maze[xx][yy]!=val-i)
dfs(xx,yy,val-i);
}
maze[x][y]=val;
printf("%d %d %d\n",x,y,val);
}
int main()
{
int n;
scanf("%d",&n);
dfs(1000,1000,n);
return 0;
}