A.百度无人车
题目大意:n个数,每将一个数减1,耗费p,问花费不超过s的情况下,最大值最小是多少
题解:都是套路,二分最大值就好了
#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b) for(int i=(a); i<(b); i++)
#define REP(i, a, b) for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 2e4 +10;
int n;
int a[N];
ll p, s;
bool solve(ll k) {
ll ret=0;
DOWN(i, n, 1) {
if (a[i]<=k) break;
if (a[i]>k) ret+=(a[i]-k)*p;
}
if (ret<=s) return true;
else return false;
}
int main() {
cin>>n;
REP(i, 1, n) {
cin>>a[i];
}
sort(a+1, a+n+1);
cin>>p>>s;
ll L=1, R=1e18;
while(L<R) {
ll M=L+(R-L)/2;
if (solve(M)) R=M;
else L=M+1;
}
cout<<L<<endl;
//cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
return 0;
}
B.百度科学家(简单)
题目大意:序列上有n个点,每个点有点权,两种操作,第一种操作,替换序列上一个x位置的点为一个新点,第二种操作,将序列上x上的点连序列l到r上的点。
点数个数<=20, 操作个数<=50
题解:由于点数和边数很小,直接暴力就好,枚举每个元素,注意这里不能用记忆化的dp,因为有可能是环。
#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b) for(int i=(a); i<(b); i++)
#define REP(i, a, b) for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 1e2 +10;
ll dp[N];
int cnt;
int x, y, l, r;
int g[N][N];
int n, m, id[N];
ll b[N];
int p[N];
int vis[N];
ll ans, tmp;
ll dfs(int cur) {
vis[cur]=1;
ll ret=b[cur];
REP(i, 1, cnt) {
if (i==cur) continue;
if (!vis[i]&&g[cur][i]) {
ret+=dfs(i);
}
}
return ret;
}
int main() {
memset(dp, -1, sizeof(dp));
cin>>n;
REP(i, 1, n) {
cin>>b[i];
id[i]=i;
}
cnt=n;
cin>>m;
REP(i, 1, m) {
cin>>p[i];
if (p[i]==0) {
cin>>x>>y;
++cnt;
b[cnt]=y;
id[x]=cnt;
}
else if (p[i]==1) {
cin>>x>>l>>r;
REP(j, l, r) {
g[id[x]][id[j]]=1;
}
}
}
ans=LL_INF;
REP(i, 1, cnt) {
memset(vis, 0, sizeof(vis));
tmp=dfs(i);
if (tmp<ans) ans=tmp;
// DBG(tmp);
}
cout<<ans<<endl;
//cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
return 0;
}
/*
3
1 10 100
3
1 1 2 3
1 2 1 3
1 3 1 2
*/
B.百度科学家(中等)
题目大意:序列上有n个点,每个点有点权,两种操作,第一种操作,替换序列上一个x位置的点为一个新点,第二种操作,将序列上位置x上的点连序列上位置l到r上的点。
点数个数
≤1e5
≤
1
e
5
, 操作个数
≤1e5
≤
1
e
5
,
∑(r−l+1)≤1e5
∑
(
r
−
l
+
1
)
≤
1
e
5
题解:由于点数正常,但边数较少,注意到每个强连通分量内的点,要么都选,要么都不选,所以tarjan缩点,枚举每个强连通分量,缩点后就变成DAG,直接上记忆化的dp就好了
#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b) for(int i=(a); i<(b); i++)
#define REP(i, a, b) for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 1e5 +10;
ll dp[N], sum[N];
int cnt;
int x, y, l, r;
int n, m, id[N], p;
ll b[N];
int vis[N];
ll ans, tmp;
vector<int> G[N];
vector<int> V[N];
int pre[N], lowlink[N], sccno[N], dfs_clock, scc_cnt;
stack<int> S;
void dfs(int u) {
pre[u] = lowlink[u] = ++dfs_clock;
S.push(u);
FOR(i, 0, V[u].size()) {
int v=V[u][i];
if (!pre[v]) {
//树边
dfs(v);
lowlink[u]=min(lowlink[u], lowlink[v]);
}
//非树边
else if (!sccno[v]) {
//非交叉边,即前向边
lowlink[u] = min(lowlink[u], pre[v]);
}
}
if (lowlink[u]==pre[u]) {
scc_cnt++;
while(1) {
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
if (x==u) break;
}
}
}
void find_scc(int n) {
dfs_clock = scc_cnt = 0;
memset(sccno, 0, sizeof(sccno));
memset(pre, 0, sizeof(pre));
for(int i=1; i<=n; i++) {
if (!pre[i]) dfs(i);
}
}
ll solve(int cur) {
vis[cur]=1;
if (dp[cur]>=0) return dp[cur];
ll ret=sum[cur];
FOR(i, 0, G[cur].size()) {
int u=G[cur][i];
if (vis[u]) continue;
ret+=solve(u);
}
return dp[cur]=ret;
}
int main() {
memset(dp, -1, sizeof(dp));
cin>>n;
REP(i, 1, n) {
cin>>b[i];
id[i]=i;
}
cnt=n;
cin>>m;
REP(i, 1, m) {
cin>>p;
if (p==0) {
cin>>x>>y;
++cnt;
b[cnt]=y;
id[x]=cnt;
}
else if (p==1) {
cin>>x>>l>>r;
REP(j, l, r) {
if (x!=j) V[id[x]].pb(id[j]);
}
}
}
find_scc(cnt);
REP(i, 1, cnt) {
sum[sccno[i]]+=b[i];
}
for(int u=1; u<=cnt; u++) {
for(int i=0; i<V[u].size(); i++) {
int v=V[u][i];
if (sccno[u]!=sccno[v]) {
G[sccno[u]].pb(sccno[v]);
}
}
}
ans=LL_INF;
REP(i, 1, scc_cnt) {
memset(vis, 0, sizeof(vis));
ll tmp = solve(i);
if (tmp<ans) ans = tmp;
}
cout<<ans<<endl;
//cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
return 0;
}
/*
3
1 10 100
3
1 1 2 3
1 2 1 3
1 3 1 2
*/
B.百度科学家(中等)
题目大意:序列上有n个点,每个点有点权,两种操作,第一种操作,替换序列上一个x位置的点为一个新点,第二种操作,将序列上x上的点连序列l到r上的点。
总元素个数
≤1e5
≤
1
e
5
, 操作个数
≤1e5
≤
1
e
5
,
∑(r−l+1)≤1e9
∑
(
r
−
l
+
1
)
≤
1
e
9
题解:由于点数正常,但边数爆炸,就需要数据结构优化了,待补。。