用 a a a 个 b b b 物品可以生产 c c c 个 d d d 物品,询问最大的 w w w,用 a a a 个 b b b 物品可以生产 w c wc wc 个 d d d 物品,物品不能无限生产。
建图。对于一个环,如果边权积大于一,则可以无限生产。增加 w w w 之后, n n n 结点环的边权积为 d 1 d 2 . . . d n × w n d_1d_2...d_n\times w^n d1d2...dn×wn。取 l o g log log,判断边权和是否大于零,然后取相反数,判断边权和是否为负。
对于 w w w ,二分答案,找到最大的 w w w 使不存在负环。 c h e c k check check有没有负环。
#include
using namespace std;
const int maxn = 1e3+10;
const int maxm = 2e3+10;
const double ops = 1e-8;
typedef long long ll;
typedef double db;
int n, m;
int head[maxn], tot;
struct edge{
int to, nxt;
db w;
}e[maxm];
void add(int a, int b, db w){
e[++tot].nxt = head[a];
e[tot].to = b;
e[tot].w = w;
head[a] = tot;
}
double ans = 0;
double dis[maxn];
int vis[maxn], cnt[maxn];
bool check(double x){
queue<int> q;
x = -log(x);
memset(vis, 0, sizeof(vis));
memset(dis, 0, sizeof(dis));
memset(cnt, 0, sizeof(cnt));
for(int i = 1; i <= n; i++)
q.push(i);
while(!q.empty()){
int u = q.front(); q.pop();
vis[u] = 0;
for(int i = head[u]; i; i = e[i].nxt){
int v = e[i].to;
db w = e[i].w;
if(dis[v] > dis[u]+w+x){
dis[v] = dis[u]+w+x;
cnt[v] = cnt[u]+1;
if(cnt[v] > n)
return true;
if(!vis[v]){
q.push(v);
vis[v] = 1;
}
}
}
}
return false;
}
int main(){
ios_base::sync_with_stdio(false), cin.tie(0);
cout << fixed << setprecision(12);
cin >> n >> m;
int a, b, c, d;
for(int i = 1; i <= m; i++){
cin >> a >> b >> c >> d;
add(b, d, -log(1.0*c/a));
}
double l = 0, r = 1;
for(int i = 1; i <= 100; i++){
double mid = (l+r)/2;
if(check(mid)){
r = mid;
ans = mid;
}
else
l = mid;
}
cout << ans;
}
构造 n n n 的排列,使 m a x ( l i s , l d s ) max(lis,lds) max(lis,lds) 最小
Dilworth定理:对偏序集< A A A, ≤ ≤ ≤ >,设 A A A 中最长链的长度是 n n n ,则将 A A A 中元素分成不相交的反链,反链个数至少是 n n n 。
对于本题,设 l i s lis lis 长度为 k k k,则 c n t ( l d s ) ≥ k cnt(lds) \ge k cnt(lds)≥k ,则 m a x ( l i s , l d s ) ≥ m a x ( k , n k ) ≥ n max(lis,lds) \ge max(k,\frac{n}{k}) \ge \sqrt n max(lis,lds)≥max(k,kn)≥n。
对于 n = 8 n = 8 n=8 ,排列为:6 7 8 3 4 5 1 2
#include
using namespace std;
int k, n;
void solve(){
cin >> n;
k = ceil(sqrt(n));
for(int i = n; i >= 1; i -= k)
for(int j = max(1,i-k+1); j <= i; j++)
printf("%d ",j);
printf("\n");
}
int main(){
int T;
cin >> T;
while(T--)
solve();
return 0;
}
给定若干点,求回归直线。
可以直接根据最小二乘法的公式求出回归直线,计算代价。
也可以三分斜率,对于每个斜率,计算出最优的 d d d ,然后计算代价。(直线是 y = k x + d y = kx+d y=kx+d)
#include
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
const int maxn = 1e6+10;
const ld eps = 1e-10;
int n;
ld a[maxn], tmp[maxn];
ld check(ld k){
ld sum = 0, res = 0;
for(int i = 1; i <= n; i++){
tmp[i] = a[i] - k*i;
sum += tmp[i];
}
sum /= n;
for(int i = 1; i <= n; i++){
res += (tmp[i]-sum) * (tmp[i]-sum);
}
return res;
}
void solve(){
cin >> n;
for(int i = 1; i <= n; i++)
cin >> a[i];
ld l = -1e9, r = 1e9;
while(r-l >= eps){
ld m1 = l+(r-l)/3.0;
ld m2 = r-(r-l)/3.0;
if(check(m1) > check(m2))
l = m1;
else
r = m2;
}
cout << fixed << setprecision(15) << check(l) << endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
cin>>t;
while(t--) solve();
}
给定长度为 n n n 的括号序列,扩展成为长度为 m m m的合法括号序列的方案数。
考虑 d p dp dp 。 f i , j , k f_{i,j,k} fi,j,k表示在 B B B 的前 i i i 位中,与 A A A 的 l c s lcs lcs 为 j j j 且左括号比右括号多 k k k 个的方案数。
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 2e2+10;
const int mod = 1e9+7;
int f[maxn][maxn][maxn];
char s[maxn];
int n, m;
void solve(){
memset(f, 0, sizeof(f));
f[0][0][0] = 1;
cin >> n >> m >> s+1;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= min(i, n); j++){
for(int k = 1; k <= i; k++){
if(s[j] == '('){
f[i][j][k] += f[i-1][j-1][k-1];
f[i][j][k] %= mod;
f[i][j-1][k-1] += f[i-1][j-1][k];
f[i][j-1][k-1] %= mod;
}
else{
f[i][j][k-1] += f[i-1][j-1][k];
f[i][j][k-1] %= mod;
f[i][j-1][k] += f[i-1][j-1][k-1];
f[i][j-1][k] %= mod;
}
}
}
if(i > n){
for(int k=1;k<=m;k++){
f[i][n][k] += f[i-1][n][k-1];
f[i][n][k] %= mod;
f[i][n][k-1] += f[i-1][n][k];
f[i][n][k-1] %= mod;
}
}
}
cout << f[m][n][0] << endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
solve();
return 0;
}
n n n 个世界,每个世界都是有向图。对于世界中的每个点 u u u,可以直接到达下一个世界的点 u u u,也可以在当前世界中由边 ( u , v ) (u,v) (u,v) 到点 v v v 。起点为 1 1 1,到达 m m m 点胜利。
询问能够胜利的最少连续世界数。
设 f i , j f_{i,j} fi,j 为能够到世界 i i i 中的点 j j j 的最晚出发点。空间不够,但 f i , j f_{i,j} fi,j 一定是由 f i − 1 , k f_{i-1,k} fi−1,k 即上个世界转移而来,可以滚掉一维。
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
const int INF = 0x3f3f3f3f;
int f[2][maxn];
int n, m;
int ans = INF;
int main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
memset(f, -INF, sizeof(f));
for(int i = 1; i <= n; i++){
int num = 0;
cin >> num;
f[(i-1)&1][1] = i;
for(int j = 1; j <= m; j++)
f[i&1][j] = f[(i-1)&1][j];//v可以由上个世界v
for(int j = 1, u, v; j <= num; j++){
cin >> u >> v;//v可以由上个世界u
f[i&1][v] = max(f[i&1][v], f[(i-1)&1][u]);
if(v == m)
if(f[i&1][v] != -INF)
ans = min(ans, i-f[i&1][v]+1);
}
}
if(ans > n)
cout << -1 << endl;
else
cout << ans << endl;
}