【无标题】

E - Blackout 2

题意:1−−n 的点为城市,n+1−−n+m的点为供电站,城市能实现供电的条件是至少连了一个发电站。现连了E条边,有q次操作,每次操作删去一条边,问此时有多少个城市能供上电

删边等于反向加边,然后把所有供电站都连接到超级源点0上(因为所有供电站都是等价的),用并查集维护连通性,以及连通块内编号即可,每次 cnt0 就是所需答案

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int fa[maxn];
map<int, pair<int, bool>> vis;
map<int, bool> notDis;
int find(int x) {
    if(x != fa[x]) fa[x] = find(fa[x]);
    return fa[x];
}
void solve() {
    int n, m, e; cin >> n >> m >> e;
    for(int i = 1; i <= n + m; i++) {
        fa[i] = i;
        if(i <= n) vis[i] = {1, false};
        else vis[i] = {0, true};
    }
    vector<pair<int, int>> edge(e + 1);
    for(int i = 1; i <= e; i++) {
        cin >> edge[i].first >> edge[i].second;
    }
    int q; cin >> q;
    vector<int> X(q + 1);
    for(int i = 1; i <= q; i++) {
        cin >> X[i];
        notDis[X[i]] = true;
    }
    int ans = 0;
    vector<int> res;
    for(int i = 1; i <= e; i++) {
        if(notDis[i]) continue;
        int tx = find(edge[i].first);
        int ty = find(edge[i].second);
        if(tx == ty) continue;
        if(vis[tx].second && vis[ty].second) {
            fa[tx] = ty;
            vis[ty].first += vis[tx].first;
        } else if(vis[tx].second || vis[ty].second) {
            if(!vis[tx].second) ans += vis[tx].first;
            if(!vis[ty].second) ans += vis[ty].first;
            fa[tx] = ty;
            vis[tx].second = true;
            vis[ty].second = true;
        } else {
            vis[ty].first += vis[tx].first;
            fa[tx] = ty;
        }
    }
    for(int i = q; i >= 1; i--) {
        res.push_back(ans);
        int tx = find(edge[X[i]].first);
        int ty = find(edge[X[i]].second);
        if(tx == ty) continue;
        if(vis[tx].second && vis[ty].second) {
            fa[tx] = ty;
            vis[ty].first += vis[tx].first;
        } else if(vis[tx].second || vis[ty].second) {
            if(!vis[tx].second) ans += vis[tx].first;
            if(!vis[ty].second) ans += vis[ty].first;
            fa[tx] = ty;
            vis[tx].second = true;
            vis[ty].second = true;
        } else {
            vis[ty].first += vis[tx].first;
            fa[tx] = ty;
        }
    }
    for(int i = (int)res.size() - 1; i >= 0; i--) {
        cout << res[i] << "\n";
    }
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
    int T = 1;
    while (T--) {
        solve();
    }
    return 0;
}

F - Monochromatic Path

题意:给定大小为 n∗m 的 01 矩阵,翻转行i的代价为ri, 翻转列j的代价为cj,最少操作多少次能存在一条路径,使得从 (1,1) 到 (n,m) 路径上颜色相同

分析:对于某行/列,操作两次等于不变,所以最多操作一次。考虑将行列的操作次数加入状态表示,则有 f[i][j][k][l]: i 行改变了 k 次, j 列改变了 l 次。
利用异或性质(0⨁1=1,1⨁1=0,0⨁0=0)实现转移。
注意一次只能改变行/列,行变则列不变,列变则行不变

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int N = 2005, inf = 1e18;
int a[N][N], r[N], c[N];
int n, m;
int f[N][N][2][2];

int main () {
    memset (f, 0x3f, sizeof f);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)    cin >> r[i];
    for (int i = 1; i <= m; i++)    cin >> c[i];
    for (int i = 1; i <= n; i++) {
        string s;   cin >> s;
        for (int j = 1; j <= m; j++) {
            a[i][j] = s[j-1] - '0';
            for (auto k : {0, 1})
                for (auto l : {0, 1}) {
                    f[i][j][k][l] = inf;
                }
        }
    }

    for (auto i : {0, 1})
        for (auto j : {0, 1})
            f[1][1][i][j] = r[1] * i + c[1] * j;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) 
            for (auto k : {0, 1})
                for (auto l : {0, 1}) {
                    if (i < n) {
                        int sta = a[i][j] ^ k ^ a[i+1][j];
                        f[i+1][j][sta][l] = min (f[i+1][j][sta][l], f[i][j][k][l] + r[i+1] * sta);
                    }

                    if (j < m) {
                        int sta = a[i][j] ^ l ^ a[i][j+1];
                        f[i][j+1][k][sta] = min (f[i][j+1][k][sta], f[i][j][k][l] + c[j+1] * sta);
                    }
                }

    int ans = inf;
    for (auto i : {0, 1})
        for (auto j : {0, 1})
            ans = min (ans, f[n][m][i][j]);

    cout << ans << endl;
}


LINE Verda Programming Contest(AtCoder Beginner Contest 263)

A - Full House

输入5个数,判断是否满足两个数相等,另外三个数相等

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 998244353;

void solve() {
    int arr[10];
    for(int i = 1; i <= 5; i++) cin >> arr[i];
    sort(arr + 1, arr + 1 + 5);
    if(arr[1] == arr[2] && (arr[3] == arr[4] && arr[4] == arr[5]) && arr[1] != arr[3] || (arr[1] == arr[2] && arr[2] == arr[3] && arr[4] == arr[5] && arr[1] != arr[4])) {
        cout << "Yes";
    } else cout << "No";
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
//    int T; cin >> T;
    int T = 1;
    while(T--) {
        solve();
    }
}

B - Ancestor

找爸爸:i的爸爸是pi,问n和1之间隔了几代
挨个找过去即可

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 998244353;

void solve() {
    int n; cin >> n;
    vector<int> arr (n + 1);
    for(int i = 2; i <= n; i++) {
        cin >> arr[i];
    }
    int idx = n;
    int res = 0;
    while(idx != 1) {
        res++;
        idx = arr[idx];
    }
    cout << res;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
//    int T; cin >> T;
    int T = 1;
    while(T--) {
        solve();
    }
}

C - Monotonically Increasing

输出所有长度为n,可选数范围为1-m的严格上升序列
全排列 + 筛选

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 998244353;

void dfs(int fa, int n, int m, int idx, string res) {
    if(idx == n + 1) {
        cout << res << "\n";
        return;
    }
    for(int i = fa + 1; i + (n - idx) <= m; i++) {
        dfs(i, n, m, idx + 1, res + to_string(i) + " ");
    }
}

void solve() {
    int n, m; cin >> n >> m;
    dfs(0, n, m, 1, "");
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
//    int T; cin >> T;
    int T = 1;
    while(T--) {
        solve();
    }
}

D - Left Right Operation

题意

进行如下操作各一次:

a 1 , a 2 , . . . , a x a_1,a_2,...,a_x a1,a2,...,ax 全替换为 L;
a n − y + 1 , . . . , a n − 1 , a n a_{n−y+1},...,a_{n−1},a_n any+1,...,an1,an全替换为 R;
0≤x,y≤n
问a的和最小为多少

分析

f1[i]表示 1 ~ i 最小可能和,f2i 表示 1 ~ i 最小可能和
则答案在 f 1 i + f 2 i + 1 f1_i+f2_{i+1} f1i+f2i+1 中取(尽可能替换更多的,故枚举分界点)

#include<bits/stdc++.h>

using namespace std;
#define ll long long
const ll mod = 998244353;


void solve() {
    ll n, l, r;
    cin >> n >> l >> r;
    vector<ll> arr(n + 2), L(n + 2), R(n + 2);
    for (int i = 1; i <= n; i++) cin >> arr[i];
    for (int i = 1; i <= n; i++) {
        L[i] = min(L[i - 1] + arr[i], i * l);
    }
    for (int i = n; i >= 1; i--) {
        R[i] = min(R[i + 1] + arr[i], (n - i + 1) * r);
    }
    ll res = LLONG_MAX;
    for (int i = 1; i <= n + 1; i++) {
        res = min(res, L[i - 1] + R[i]);
    }
    cout << res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
//    int T; cin >> T;
    int T = 1;
    while (T--) {
        solve();
    }
}

E - Sugoroku 3

题意

1 ~ n−1 每个上都有一个骰子🎲,数字范围为 0 ~ a i a_i ai
在i上掷出的数字为j,则走到i+j
求在n时的掷骰子次数的期望值

分析

f[i]表示i到n的期望次数(相当于逆推,以n为起点,则f[n]=0)

转移就是枚举每一种可能的步数:f[i]=(1+f[i+1]+f[i+2]+…+f[i+a[i]])/a[i]+1 (最开始的1表示投掷到了0)

后缀和优化区间求和

#include<bits/stdc++.h>

using namespace std;
#define ll long long
const ll mod = 998244353;
ll qpow(ll a, ll x) {
    ll res = 1;
    while(x) {
        if(x & 1) res = res * a % mod;
        a = a * a % mod;
        x >>= 1;
    }
    return res;
}

void solve() {
    int n; cin >> n;
    vector<ll> A(n + 1), dp(n + 1), suf(n + 2);
    for(int i = 1; i < n; i++) cin >> A[i];
    for(int i = n - 1; i >= 1; i--) {
        dp[i] = ((suf[i + 1] - suf[i + A[i] + 1] + A[i] + 1) % mod + mod) % mod * qpow(A[i], mod - 2) % mod;
        suf[i] = (suf[i + 1] + dp[i]) % mod;
    }
    cout << dp[1];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
//    int T; cin >> T;
    int T = 1;
    while (T--) {
        solve();
    }
}

ARC 144

C - K Derangement

细节的构造题,一看这题,立马就想到了构造方法,但是还是有小瑕疵。

例如:9 2

我们先可以按2*k分组,依次分成【1,4】,【5,8】,【9,9】

如果有多余的组别,可与最后一组2*k合并【1,4】,【5,9】

此时我们只要每个组别的数字前k个数+k,后k个数-k即可,此时数列即为

3,4,1,2,7,8,9,5,6 这就是最后构造结果;

但是还要一种特殊情况(我当时就是没注意到,呜呜呜呜…)

例如 :15 4

按上面方法构造出的即是:5 6 7 8 9 10 11 12 13 14 15 1 2 3 4

但是你可以发现最后一个组别1,2…有的数还可以在这个组别内继续往前调整位置,使得字典序更小,变成 5 6 7 8 1 2 3 12 13 14 15 4 9 10 11

做题技巧:在构造题完全没思路的时候,可以尝试打表看看规律

例如官方题解:
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
//#define rep(i,a,b) for(int i=a;i<=b;i++)
//#define rep2(i,a,b) for(int i=a;i>=b;i--)
///* run this program using the console pauser or add your own getch, system("pause") or input loop */
#define sc(x) scanf("%d",&x)
#define sl(x) scanf("%lld",&x)
#define ll long long
#define pb push_back
typedef pair<int,int>PII;
const int Max=1e6+5;
const ll INF=1e15+5;
int a[Max],n;
int main(){
    sc(n);int k;
    sc(k);
    if(n<k*2){
        printf("-1\n");
        return 0;
    }
    int num=n/(2*k);
    int ans=1,i=1;
    if(n%(2*k)==0){
        while(num>0){
            for(int j=0;j<k;j++){
                a[i+j+k]=ans;
                ans++;
            }
            for(int j=0;j<k;j++){
                a[i+j]=ans;
                ans++;
            }
            i+=2*k;
            num--;
        }
        for(int i=1;i<=n;i++){
            printf("%d ",a[i]);
        }
        return 0;
    }
    while(num>=2){
        for(int j=0;j<k;j++){
            a[i+j+k]=ans;
            ans++;
        }
        for(int j=0;j<k;j++){
            a[i+j]=ans;
            ans++;
        }
        i+=2*k;
        num--;
    }
    int pre=abs(n-k);int len=n;
    int j;int tmp,temp,cnt;
    for(j=pre;j>=i;j--){
        a[j]=len;
        if(len==pre){
            tmp=j;
            cnt=len;
        }
        len--;
    }
    temp=a[i+k-1];
    // cout<<tmp<<' '<<cnt<<"----\n";
    for(int m=i+k;m<=tmp;m++){
        a[m]=ans;ans++;
    }
    for(int j=pre+1;j<=n;j++) a[j]=ans,ans++;
    for(int j=n;j>=1;j--){
        if(cnt<=temp) break;
        a[j]=cnt;
        cnt--;
    }
    // if(!flag) printf("-1\n");
    // else{
        // for(int j=i;)
        for(int i=1;i<=n;i++){
            printf("%d ",a[i]);
        }
    // }
}

Educational Codeforces Round 133 (Rated for Div. 2)

A - 2-3 Moves(Rated 800)

题意

有一数轴,hy在坐标0上,hy每次有四种操作:

  • idx ± \pm ± 2
  • idx ± \pm ± 3

问:最少操作几次,hy可以到达左边n

分析

贪心的角度来看,肯定优先考虑+3使步数更少,当然一直+3可能无法到达n点,现在来分析一下到达n点的“最后一公里”

  • n % 3 = = 1 n\%3==1 n%3==1

    前面我们一直选择+3,最后到达了n-1的位置,一种选择是(+3, -2) = +1,那么答案就是 n / 3 + 2 n/3+2 n/3+2,有一种更优的选择是把之前的一次+3改为+2,这样由n-1位置变为n-2位置,只需要(+2)就可以到达n点,答案是 n / 3 + 1 n/3+1 n/3+1,因为我们只有一部额外操作(+2),这种情况的前提条件是前面至少有一次+3操作,所以特判1。

  • n % 3 = = 2 n\%3==2 n%3==2

    直接往后+2即到达n点

Code
void solve() {
   	int n; cin >> n;
    if(n == 1) cout << 2 << "\n";
    else if(n % 3 == 0) cout << n / 3 << "\n";
    else if(n % 3 == 1) cout << n / 3 + 1 << "\n";
    else if(n % 3 == 2) cout << n / 3 + 1 << "\n";
}

B - Permutation Chain(Rated 800)

题意

构造一个最长排列链,输出这条链。

排列链:

1 每一个结点都是一个[1,n]的排列

2 a[i+1]必须是由a[i]交换两个元素得到

3 a[i+1]中固定点的个数必须严格小于a[i]

固定点的数量:

数组b中,b[j] = j的个数

分析

要求排列链结点的固定点的数量是严格递减的,那么最理想的状态就是每次-1,从n变为0。所以初始状态必须是b[1,2,3…n],固定点的数量为n

但是经过一次变换,我们发现n->n-2,无法变为n-1。

第二次变换开始,我们只需要把b[i]与b[1]互换,每次固定点数-1,这样总共我们就可以共造出长度为n的最长链(n, n-2, n-3, ,1,0)

pic

Code
void solve() {
    int n; cin >> n;
    vector<int> arr(n);
    iota(arr.begin(), arr.end(), 1);
    cout << n << "\n";
    for(int i = 0; i < n; i++) {
        swap(arr[i], arr[0]);
        for(int j = 0; j < n; j++) {
            cout << arr[j] << " \n"[j == n - 1];
        }
    }
}

C - Robot in a Hallway

题意

有一个2*m的地图,每个格子上有一个封印,当格子解除封印后,才允许进入格子,且一个格子不允许经过两次。

hjm是一个不服输的人,他想要从(1,1)最快的走完所有格子,以彰显他在212的地位。

sct想知道他用了多久完成了这个目标,但是sct太菜了,所以他求助你们,帮他算出hjm用了多久走完所有格子。

分析

​ 图源网络

总的来说,走完全程只有一种情况,那就是↓↑走到(i,j),然后从(i,j)出发走U型。

那我们枚举U型起点(i,j),累计答案需要知道(1,1)走到(i,j)(i,j)走完U型分别需要多长时间

dp[i][j]表示从(i,j)出发走U型到(i,j^1)的需要的最大等待(解锁)时间

Code
void solve() {
    int m; cin >> m;
    vector<vector<int>> lock(m + 2, vector<int>(3));
    vector<vector<int>> dp(m + 2, vector<int>(3));
    for(int j = 0; j <= 1; j++) {
        for (int i = 1; i <= m; i++) {
            cin >> lock[i][j];
        }
    }
    lock[1][0] = -1;
    for(int i = m; i >= 1; i--) {
        for(int j = 0; j <= 1; j++) {
            dp[i][j] = max(lock[i][j] + 1, //解锁之后才允许进入,所以最早进入ij的时间是loak[i][j]+1
                           max(dp[i + 1][j] - 1, // 因为(i,j)走到(i+1,j)需要1的时间,所以(i+1,j)出发的最大等待时间,相当于-1
                               /*为什么是比较lock[i][j^1]: 因为1层走U,需要经过[i][2],虽然2层走U,不需要经过[i][1],但是不影响答案累计
                               	为了减少讨论,直接认为他们都需要走到[i][j^1]
                               */
                               lock[i][j ^ 1] - (2 * (m - i + 1) - 1 - 1)
                               /*因为求的是等待时间,解锁减去走到[i+1,j]需要的时间即为需要在[i+1,j]上等待的时间*/)));
        }
    }
    // pre: 走到(i,j)前一共花费了多长时间
    int res = INT_MAX, pre = 0;
    int x = 1, y = 0;
    for(int i = 1; i <= m; i++) {
        // 2 * (m - i + 1) - 1 := 点-1=边
        // 为什么上面说直接让他们都走到[i][j^1]是合法的:因为答案累计是全程的等待时间+路径时间
        // 虽然[i][1]->[i][2]->U->[i][1]重复了,但是pre时间就包含了[i][1]的时间,所以答案取max不影响答案统计
        // 即前面的时间答案后面的等待时间,等价于后面不需要等待
        res = min(res, max(pre, dp[x][y]) + 2 * (m - i + 1) - 1);
        y ^= 1;
        pre = max(pre + 1, lock[x][y] + 1);
        x++;
        if(i == m) {
            res = min(res, pre);
        } else {
            pre = max(lock[x][y] + 1, pre + 1);
        }
    }
    cout << res << "\n";
}

D - Chip Move

题意

给定一个n和一个k,初始时从0位置开始,第i步可以选择长度为(k + i - 1)正倍数的步长。问1~n中的每个x,从0走到x有多少种方案。

分析

朴素的状态设计可以设dp[j][i]表示经过j步,到达i的方案数。

对于dp[j][i],有两种转移

  1. 继续加k+i-1,即增大第j步的倍数,dp[j][i + k + i - 1] += dp[j][i]
  2. 继续走一步,dp[j + 1][i + k + i] += dp[j][i]

因为只设计j,j+1两个状态,所以可以利用滚动数组压缩空间

Code
const int mod = 998244353;
void solve() {
    int n, k; cin >> n >> k;
    vector<vector<int>> dp(2, vector<int>(n + 2));
    vector<int> res(n + 1);
    int idx = 1;
    int m = 2 * sqrt(n);
    dp[0][0] = 1;
    for(int j = 0; j < m; j++) {
        fill(dp[idx].begin(), dp[idx].end(), 0);
        idx ^= 1;
        for(int i = 0; i + k + j <= n; i++) {
            dp[idx][i + k + j] = (dp[idx][i + k + j] + dp[idx][i]) % mod;
            // 初始状态只能走k的倍数
            if(i == 0 && j == 0) continue;
            dp[idx ^ 1][i + k + j + 1] = (dp[idx ^ 1][i + k + j + 1] + dp[idx][i]) % mod;
        }
        // 累计idx步走到i的方案数
        for(int i = 1; i <= n; i++) res[i] = (res[i] + dp[idx][i]) % mod;
    }
    for(int i = 1; i <= n; i++) {
        cout << res[i] << " ";
    }
}

牛客暑假(一)

G-Lexicographical Maximum

题意

给定一个整数n,输出1-n中字典序最大的数

分析

读入一个字符串 S ,长度为 len
当 S 的前 len-1 位都是 9 时输出 S 本身
否则输出 len-1 个 9 即可

#include<bits/stdc++.h>
using namespace std;
 
 
int main()
{	
	string S;
	cin>>S;
	int len=S.length();
	bool flag=true;
	
	if(len==1){
		cout<<S<<endl;
		return 0;
	}
	
	for(int i=0;i<len-1;i++) {
		if(S[i]!='9') {
			flag=false;
			break;
		}
	}
	
	if(flag) cout<<S<<endl;
	else for(int i=0;i<len-1;i++) cout<<'9';
	cout<<endl;
	
}

A-Villages:Landlines

题意

在一维坐标上存在着 n 个建筑物,其中一个为发电站,剩余 n-1 个为用电建筑物 ,你可以建造一些电塔来联通发电站与建筑物。发电站与用电建筑物都有自己的接收范围,在该范围中建造电塔使该建筑物联入电力系统(无需电线),电塔之间的连接需要电线,当所有建筑物联入时,需要最短的电线长度。

分析

将所有区间按照左端点进行排序,维护一个最右边的端点,将所有左端点与该右端点比较
当左端点小于等于最右端点时:
说明两个区间是相交的,未产生间隙,不管,更新最右端点为更大的右端点
ed=max(ed,seg.second)
当左端点大于最右端点时:
说明两个区间产生了间隙,我们将间隙累加到答案中,更新最右端点
ans+=seg.first-ed
ed=seg.second

#include<bits/stdc++.h>
using namespace std;
 
typedef struct {
	int st;
	int ed;
}PII;
 
bool cmp(PII a,PII b)
{
	return a.st<b.st;
}
 
 
int main() {
	
	int n,st,ed,ans=0,cen,rad;
	cin>>n;
	
	PII segs[n];
	
	for(int i=0;i<n;i++) {
		cin>>cen>>rad;
		segs[i].st=cen-rad;
		segs[i].ed=cen+rad;
	}
	
	sort(segs,segs+n,cmp);
	ed=segs[0].ed;
	
	for(int i=1;i<n;i++) {
		if(segs[i].st<=ed) ed=max(ed,segs[i].ed);
		
		else{
			ans+=segs[i].st-ed;
			ed=segs[i].ed;
		}
	}
	
	cout<<ans<<endl;
	
	
	
}

D.Link with Game Glitch

题意

有m个配方,每个配方可以使用 k × a i k × a_i k×ai b i b_i bi种原料生成 k × c i k × c_i k×ci d i d_i di种原料。现在要求在k上乘一个权值w,使得所有配方间不存在可以循环生成无限物品的局面。要求最大化m。

分析

显然要生成无限物品,需要存在一个环且沿该环生成一轮得到的物品数目比原来更多,即为环上满足所有的边有k ( c [ i ] − a [ i ] ) > 0

#include <bits/stdc++.h>
using namespace std;

const int N = 2e3 + 10, MOD = 1e9 + 7;

struct node{ 
	int v; 
	double w; 
};

vector<node> g[N];
int n, m, cnt[N];
double dis[N];
bitset<N> vis;

bool check(double x){
    queue<int> q;
    x = -log(x);
    vis.set();
    memset(dis, 0, sizeof(dis));
    memset(cnt, 0, sizeof(cnt));
    for(int i = 1; i <= n; i++) q.emplace(i);
    while(q.size()){
        int u = q.front(); q.pop();
        vis.reset(u);
        for(auto nxt : g[u]){
            int v = nxt.v;
            double w = nxt.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.emplace(v);
                    vis.set(v);
                }
            }
        }
    }
    return false;
}   

void solve(){
    cin >> n >> m;
    for(int i = 1; i <= m; i++){
        int a, b, c, d; cin >> a >> b >> c >> d;
        g[b].emplace_back(node{d, -log((1.0 * c) / a)});
    }
    double l = 0, r = 1;
    for(int i = 1; i <= 300; i++){
        double mid = (l + r) / 2;
        if(check(mid)) r = mid;
        else l = mid;
    }
    cout << fixed << setprecision(12) << l << endl;
}

signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    
    int t = 1; //cin >> t;
    while(t--) solve();
    return 0;
}

G. Link with Monotonic Subsequence

题意

要求构造一个长度为n nn的序列,使得序列的max ⁡ ( lis ( p ) , lds ( p ) ) 尽可能的小

分析

狄尔沃斯定理:对偏序集< A , ≤ > ,设A中最长链的长度是n,则将A 中元素分成不相交的反链,反链个数至少是n。

那么有lds ( p ) = c n t ( lis§ ) ,那么显然max ⁡ ( lis ( p ) , lds ( p ) ) ≥ max ⁡ ( k , k n ) ≥ n 。因此直接构造 n \sqrt{n} n 个块即可

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e5 + 10, MOD = 1e9 + 7;


void solve(){
    int n = 0; cin >> n;
    int b = ceil(sqrt(n));
    while(n > 0){
        for(int i = max(1ll, n - b + 1); i <= n; i++) cout << i << " ";
        n -= b;
    }
    cout << endl;
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0);
    int t = 1; cin >> t;
    while(t--) solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LOTRcsl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值