【补】2022.7.22———多校联测【2022年多校冲刺NOIP联训测试4】


$Write In Front$

感觉多校联测的题比较水?

成绩综述

【补】2022.7.22———多校联测【2022年多校冲刺NOIP联训测试4】

$112 / 174$,我菜菜

 

T1 甲国的军队

 

大水题,先打表找个规律

然后sort一下,按照B[i]与A[i]的差值降序,然后模拟

T1


#include <iostream>
#include <iomanip>
#include <algorithm>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define _MARK "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ " "
#define Endl cout << '/n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define CASE 12
#define N 100005
#define MAXNUM 1000000002
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false);cin.tie(NULL), cout.tie(NULL);}
long long T, n, final_ans, now;
struct node{
    long long die, need, derta;
}a[N];
inline bool comp(node A, node B){
    if(A.derta != B.derta) return A.derta > B.derta;
    else if(A.die != B.die) return A.die > B.die;// 这应该没影响
    else return A.need > B.need;
}
inline void Clean(){now = final_ans = 0;}
inline void work(){
    Clean();
    // T1 额
    // 这个就是derta是吗
    // die <= need
    cin >> n;
    for(re i = 1 ; i <= n ; ++ i){
        cin >> a[i].die >> a[i].need;
        a[i].derta = a[i].need-a[i].die;
    }
    sort(a+1, a+n+1, comp);
    for(re i = 1 ; i <= n ; ++ i){
        if(now >= a[i].need){
            now -= a[i].die;
            continue;
        }
        final_ans += (a[i].need-now), now = a[i].need;
        now -= a[i].die;
    }
    cout << final_ans << '/n';
}
// #define IXINGMY
char_phi main(){
    #ifdef IXINGMY
        FBI_OPENTHEDOOR(a);
    #endif
    Fastio_setup();
    cin >> T;
    while(T --){
        work();
    }
    return GMY;
}

 

T2 虚弱

二分。具体看Chen_jr的博客

提示:二分可以“随机化”

但是NIMA像这种6位数精度的就算了吧

然后还有技巧,clock()大法

我这个程序常数大,所以用clock()提前终止二分,本来60然后就A掉了

T2
 

#include <iostream>
#include <iomanip>
#include <cmath>
#include <random>
#include <ctime>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define _MARK "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ " "
#define Endl cout << '/n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define eps 0.000000000001
#define N 200005
#define MAXNUM 10005
#define mod %
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false);cin.tie(NULL), cout.tie(NULL);}
int n, ner;
long long tim;
double final_ans(11451419.0);
double a[N], kl[N];
inline bool check(double number){
    for (re i = 1 ; i <= n ; ++ i)
        kl[i] = a[i] + number;// 注意是加 因为有负数
    double mn(kl[1]), mx(kl[1]), now_min(0), now_max(0);
    for (re i = 1 ; i <= n ; ++ i){
        now_min += kl[i], now_max += kl[i];
        mn = MIN(mn, now_min), mx = MAX(mx, now_max);
        if (now_min > 0) 
            now_min = 0;// 前面的不要了开另一个区间,因为前面的加起来已经大于0,后面咋变小都没有重新开一个区间、now_min=0更优,所以重新开一个区间
        if (now_max < 0) 
            now_max = 0;
    }
    double harmony = MAX(abs(mn), abs(mx));
    if (harmony < final_ans) final_ans = harmony;
    if (mx < 0) return false;
    // mx >= 0 了
    if (mn > 0) return true;// 最小的都大于0,那么根本不够小,应该减得更小
    // mn <= 0 了
    if (abs(mn) < abs(mx)) return true;// 应该更小一些以使得abs(mn)更大,也就是x更大
    // abs(mn) >= abs(mx);
    return false;
}
inline void work(){
    random_device rd;// 来吧!随机化!!!
    mt19937 myrand(rd());
    // 既然虎哥都说惹。。那么我就先改题吧
    // 模拟退火先放一下)
    // T2
    // sum为前缀和数组,那么题意:
    // 找到一个最小的ans,使得任意的1 <= j <= i <= n, sum[i]-sum[j] - (i-j)*x <= ans
    // 所以拆开
    // sum[i]-i*x - (sum[j]-j*x) <= ans
    // 所以要找的ans:
    // ans = MAX{sum[i]-i*x} - MIN(sum[j]-j*x)
    // 注意到      ↑                   ↑   各是关于x的两个一次函数
    // 这本来是一个单谷函数,可以用爬山算法,模拟退火额也行
    // 然而可以三分。但是蒟蒻不会三分
    // 所以考虑二分(考虑陈钩R(划掉))
    // 考虑两个极值(最初时)。一个是最大的记为mxx,一个是最小的记为mnn
    // 两种情况。
    // Case 1: mxx > 0 && mnn > 0
    // 这种情况下我们会希望让mnn一直减小到0之后接着减小,同时mxx当然也会减小,但是abs()是个单谷的,所以过了0这个界限之后abs(mnn)有朝一日会再减一点点点就会比比 abs(mxx)大,这里就是我们想要的谷点
    // 所以二分减去多少x, 找到那个谷点
    // 这种情况启示我们二分的时候要边界应该包括正数
    // Case 2: mxx > 0 && mnn < 0
    // 这种情况下也是,和上面差不多,这个可以再分成两种情况,即 ↓
    // 这种情况启示我们二分的时候边界应该包括负数。因为初始时可能abs(mnn) > abs(mxx),也可能【 < 】。【 > 】 的话就肯定是要让mnn增加也就是让abs(mnn)减小,让abs(mxx)增大,然后看拐点
    // 然后剩下的就是代码实现了 代码实现还是比较ex的。。。(对于本蒟蒻而言)
    // 那个“y”图像,一个是mx,一个是abs(mn)
    cout.setf(ios::fixed), cout.precision(6);
    cin >> n; ner = (sqrt(sqrt(n))+1) * 100000;
    for (re i = 1 ; i <= n ; ++ i)
        cin >> a[i];
    double l = -10000.0, r= 10000.0, mid;
    // int times(0);
    // long long tim = clock();
    // while (r-l >= eps) {
    //     mid = ((l + r) / 2.0) + (SQ(SQ(SQ(SQ(SQ(SQ(SQ(SQ((double)myrand())))))))));// -x, 过去后是+x
    //     mid -= SQ(SQ(SQ(SQ(SQ(SQ(SQ(SQ((double)myrand()))))))));
    //     cout << l << _ << mid << _ << r << '/n';
    //     if(check(mid) == true) r = mid;// 值应当更小,x应当更大
    //     else l = mid;
    //     times ++;
    //     if(times == 50000) break;
    // }
    
    while (r-l >= eps) {
        // mid = ((l + r) / 2.0) + myrand()*1e-9;// -x, 过去后是+x
        // mid -= myrand()*1e-8;
        // cout << l << _ << mid << _ << r << '/n';
        mid = ((l + r) / 2.0);
        if(check(mid) == true) r = mid;// 值应当更小,x应当更大
        else l = mid;
        // times ++;
        // if(times == 1000) break;
        if((double)(clock()-tim) * 1000 / CLOCKS_PER_SEC >= 2995) break;
    }
    // cout << clock() - tim << endl;
    cout << final_ans << '/n';
}
// #define IXINGMY
char_phi main(){
    tim = clock();
    #ifdef IXINGMY
        FBI_OPENTHEDOOR(a);
    #endif
    Fastio_setup();
    work();
    return GMY;
}

 

T3 萨鲁曼的半兽人

joke3579的SPFA

我把我俩的聊天记录粘在这里

mad,luog什么复制规则,从下往上是时间顺序,倒着看吧。。

I: 懂

I: joke3579:终点自然就是n字符的右侧

joke3579:?因为你在字符间建点

I:还是您的SPFA

I: 为啥输出的是dis[n+1]?

I: “`printf(“%lld/n”,dis[n+1]-1);“`

07-24 21:35

I: joke

I: 谢谢

I: 泄泄

I: 泄泄joke

I: ylrc

joke3579:也不长 就当kmp了

joke3579:对的

I: joke,您用了马拉车?

I: joke 有什么处理回文子串的好方法么

joke3579:?manacher可以在O(n)的复杂度内找出所有极长回文子串

I: 然而处理子回文串不是n^3的吗

I: 好像真是

I: 唔

joke3579:为啥是最短路? -> 因为你要挑出最少的字符串

joke3579:i和i-1为啥要建? -> 因为如果你一直往前莽 总会有向前走不了的时候

I: i和i-1为啥要建?

I: 为啥是最短路?

joke3579:就在起始字符左边和终止字符右边建

I: others?

joke3579:因为有长度为1的回文串,你自己向自己建边显然fake

I: 蒟蒻比较菜,谢谢大佬耐心gg

I: 为啥让【】与【】建?

joke3579:?什么叫物理意义

I: 巨,不过根本没看出建边的物理意义啊?

I: 巨,大佬的每一言每一语都重如千钧,像我这样的蒟蒻是无法短时间内看懂的,谢谢大佬!

joke3579:这是我给jjdw发的讲解

joke3579:拿T3当例题 首先我们有一个f[]数组,记f[i]为以i位置的字符为左端点向右延申极长回文串对应的右端点 然后考虑在字符间建边 因为有长度为1的回文串,你自己向自己建边显然fake 然后从i向f[i]+1建一条长度为1的边,保证了如果区间连续的话最短路就是正确结果 我们发现如果一直莽根本找不到正确结果 怎么办呢?考虑反悔 向i到i-1建一条长度为0的边

joke3579:因为这是道最小覆盖问题

I: 昨天的T3,为什么能用SPFA啊?

07-24 20:05

I: 还在吗joke

T3
 

#include <iostream>
#include <iomanip>
#include <queue>
#include <cstring>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define _MARK "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ " "
#define Endl cout << '/n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 100005
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false);cin.tie(NULL), cout.tie(NULL);}
int n, R, mid, star_cnt;
char sr[N];
bool inq[N];// inside_queue
int p[N<<1], f[N], dis[N], head[N];
struct star{
    int v, w, nxt;
}e[N<<1];
queue<int> q;
string a;
// T3 萨鲁曼的半兽人
// joke的SPFA,真的diao
// 所以用joke 的SPFA来了
inline void star_add(int u, int v, int w){e[++ star_cnt].v=v, e[star_cnt].w=w, e[star_cnt].nxt=head[u], head[u]=star_cnt;}
inline void Clean(){
    star_cnt = mid = R = 0; a.clear();
    for (re i = 1 ; i <= n+3 ; ++ i)
        sr[i] = head[i] = f[i] = p[i] = inq[i] = 0, dis[i] = 1145141919;
}
inline void spfa(int x){
    q.push(x), dis[x] = 0, inq[x] = true;
    while (q.empty() == false){
        x = q.front(), q.pop();// 一x多用(doge)
        inq[x] = false;
        for (re i = head[x] ; i ; i = e[i].nxt){
            if (dis[e[i].v] > dis[x]+e[i].w){
                dis[e[i].v] = dis[x]+e[i].w;
                if (inq[e[i].v] == false)
                    q.push(e[i].v), inq[e[i].v]=true;
            }
        }
    }
}
inline void work(){
    a += '(';
    for (re i = 1 ; i <= n ; ++ i)
        a += '#', a += sr[i];
    a += '#', a+= ')';
    for (re i = 1 ; i <= a.size()-2 ; ++ i){
        p[i] = ( (R > i) ? (MIN(p[(mid<<1)-i], R-i)) : (1));
        while(a[i+p[i]] == a[i-p[i]]) p[i] ++;
        if (i+p[i] > R)
            R = i+p[i], mid = i;
    }
    for (re i = 1 ; i <= a.size()-2 ; ++ i){
        if ( ((i-1) & 1) == 1 )
            f[(i>>1) - (p[i]>>1) + 1] = MAX(f[(i>>1) - (p[i]>>1) + 1], (i>>1) + (p[i]>>1) - 1);
        else if (p[i] > 1)
            f[((i-1)>>1) - ((p[i]-1)>>1) + 1] = MAX(f[(i-1)>>1 - ((p[i]-1)>>1) + 1], ((i+1)>>1) + ((p[i]-1)>>1) - 1);
    }
    star_add(1, f[1]+1, 1);
    for (re i = 2 ; i <= n ; ++ i)
        star_add(i, f[i]+1, 1), star_add(i, i-1, 0);// joke/
                                    “一直往前莽可能到不了”
    spfa(1);
    // MARK;
    cout << dis[n+1]-1 << '/n';///
           字符之间建边,点会多一个(手模) 减一很简单,他问的是“连接”,表明过了dis[n+1]个边dis[n+1]+1个点,其中每两个点是一个“子回文串”so有dis[n+1]个,把他们连接起来要有dis[n+1]-1
}
/*
obocodo
abcdef
abcdcba
*/
// #define IXINGMY
char_phi main(){
    #ifdef IXINGMY
        FBI_OPENTHEDOOR(a);
    #endif
    Fastio_setup();
    for (re i = 1 ; i <= 100000 ; ++ i) dis[i] = 1145141919;
    while (true){
        Clean();
        cin >> sr+1; n = strlen(sr+1);
        // cout << n << _ << strlen(sr+1) << '/n';
        if(n == 0) break;
        work();
    }
    return GMY;
}

 

T4 序列

先吃饭去

a

a

a

原创文章,作者:wdmbts,如若转载,请注明出处:https://blog.ytso.com/277090.html

(0)
上一篇 2022年7月26日
下一篇 2022年7月26日

相关推荐

发表回复

登录后才能评论