• NOI.ac 模拟赛20181103 排队 翘课 运气大战


    题解

    排队

    20% 1n20,1x,hi201le nle 20, 1le x,h_ile 20 随便暴力

    50% 1n2000,1x,hi1e91le n le 2000, 1le x,h_ile 1e9 枚举把哪个定成中位数

    100% 1n2e5,1x,hi1e91le nle 2e5,1le x,h_ile 1e9

    先把高度排序。贪心地修改。最好的方法是把最中间的人变成xx。那么就把左边比他高的或者右边比他矮的都改成xx就好啦。

    #include <bits/stdc++.h>
    using namespace std;
    #define LL long long
    const int MAXN = 200005;
    int n, x, h[MAXN];
    LL Ans;
    int main()
    {
        scanf("%d%d", &n, &x);
        for(int i = 1; i <= n; i++)
            scanf("%d", &h[i]);
        sort(h + 1, h + n + 1);
        int l = n>>1, r = (n>>1)+2;
        Ans = abs(h[(n>>1)+1]-x), h[(n>>1)+1] = x;
        while(l >= 1 && r <= n)
        {
            Ans += max(h[l]-h[l+1], 0), h[l] = min(h[l], h[l+1]); l--;
            Ans += max(h[r-1]-h[r], 0), h[r] = max(h[r], h[r-1]); r++;
        }
        printf("%lld
    ", Ans);
    }
    

    翘课

    10% 2n,m500,k=12le n,mle 500,k=1

    20% 2n,m200000,k=12le n,mle 200000,k=1 暴力

    40% 2n,m2000,k=22le n,mle 2000,k=2 找环

    70% 2n,m2000,k&lt;n2le n,mle 2000,k&lt;n 每次跑点判断

    100% 2n,m200000,1k&lt;n2le n,mle 200000,1le k &lt;n 倒着删

    考虑先把所有边加进去,再倒过来删边。

    对于每个点,我们考虑要不要删掉它,删掉它表示它连接着选中的点的度小于 K 了。删完它之后我们把它邻居的度都–,然后再看它的邻居要不要被删掉。这样预处理把每个点判一次要不要删。

    之后删一条边也是删完就判断连接的两个点需不需要被删掉。因为每个点只会被删一次,所以整体还是O(n)的。

    题目做法可能多样,但思路应该都是倒过来。

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN = 200005;
    const int MAXM = 200005;
    inline void read(int &num)
    {
        char ch; int flag=1;
        while(!isdigit(ch=getchar()))if(ch=='-')flag=-flag;
        for(num=ch-'0';isdigit(ch=getchar());num=num*10+ch-'0');
        num*=flag;
    }
    
    int n, m, k, d[MAXN], Ans;
    int fir[MAXN], to[MAXM<<1], nxt[MAXM<<1], cnt = 1;
    
    inline void Add(int u, int v)
    {
        to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt;
        to[++cnt] = u; nxt[cnt] = fir[v]; fir[v] = cnt;
    }
    
    bool flg[MAXN], used[MAXM<<1];
    
    inline void del(int u)
    {
        if(!flg[u]) return;
        flg[u] = 0; Ans--;
        for(int i = fir[u]; i; i = nxt[i])
    		if(used[i^1] && (used[i^1] = 0, --d[to[i]] == k-1)) del(to[i]);
    }
    
    bool vis[MAXN];
    inline bool dfs1(int u)
    {
    	if(vis[u]) return flg[u];
    	vis[u] = 1;
    	flg[u] = 1; Ans++;
        for(int i = fir[u]; i; i = nxt[i])
    		if(dfs1(to[i]))
    			++d[u], used[i] = 1;
        if(d[u] >= k) return 1;
    	del(u);
    	return 0;
    }
    int U[MAXM], V[MAXM], ans[MAXM];
    int main ()
    {
        read(n), read(m), read(k);
        for(int i = 1; i <= m; i++)
            read(U[i]), read(V[i]), Add(U[i], V[i]);
        for(int i = 1; i <= n; i++)
    		if(!vis[i]) dfs1(i);
        for(int i = m; i >= 1; i--)
        {
            ans[i] = Ans;
            if(used[i<<1] && (used[i<<1] = 0, --d[U[i]] == k-1)) del(U[i]);
            if(used[i<<1|1] && (used[i<<1|1] = 0, --d[V[i]] == k-1)) del(V[i]);
        }
    	for(int i = 1; i <= m; i++) printf("%d
    ", ans[i]);
    }
    
    

    运气大战

    20% n10,q10nle 10,qle 10 暴力

    另30% n1000,q100nle 1000,qle 100 这个点我也不记得为什么放了,但看起来很多人只拿到了50所以可能还是有点意义的?

    另20% n30000,q500nle 30000,qle 500 nq 的 dp

    100% 2n30000,1q10000,1wi,ri1e6,1ai̸=bin2le nle 30000,1le qle 10000,1le w_i,r_i le 1e6,1le a_i ot=b_i le n

    由于排序不等式,我们尽量想顺序放。两边都排序。

    由于 n 个不能配的干扰,又不能完全顺序放。

    有个结论,最后匹配出第 i 个人的运气值是第 j 个的话,ij2|i-j|le2。这个结论从最小化逆序对的个数来看,自己把附近几个线连起来画一画证明一下。

    这样就可以用 dp[i]表示到 i 为止所有配好的最优答案。计算的时候需要用到前三轮的答案然后讨论一下。这个是 O(nq)的,可以过70%。

    用线段树记录区间答案。区间记录这样的信息:把这个区间前0-2个和后0-2个元素去掉的答案,用3x3的矩阵维护。这样复杂度是O(qlogn)。

    由于出题人良心发现将时限改为3s,常数小的nq算法也能过1.5s左右,下贴AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define LL long long
    const int MAXN = 30005;
    const LL INF = 1ll<<60;
    inline void read(int &num)
    {
        char ch; int flag=1;
        while(!isdigit(ch=getchar()))if(ch=='-')flag=-flag;
        for(num=ch-'0';isdigit(ch=getchar());num=num*10+ch-'0');
        num*=flag;
    }
    struct node
    {
    	int val, id;
    }A[MAXN], B[MAXN];
    LL dp[MAXN], cost[MAXN][2];
    int n, q, posa[MAXN], posb[MAXN];
    
    inline LL Calc(int i, int j)
    {
    	if(i < 1 || j < 1 || i > n || j > n || A[i].id == A[j].id) return -INF;
    	return 1ll * A[i].val * B[j].val;
    }
    
    inline void Upd(int i)
    {
    	if(i < 1 || i > n) return;
    	cost[i][0] = Calc(i, i+1) + Calc(i+1, i);
    	cost[i][1] = max(Calc(i, i+1) + Calc(i+1, i+2) + Calc(i+2, i),
    					Calc(i, i+2) + Calc(i+1, i) + Calc(i+2, i+1));
    }
    
    inline bool cmp(node x, node y) { return x.val < y.val; }
    
    int main ()
    {
    	read(n), read(q);
    	for(int i = 1; i <= n; i++) read(A[i].val), A[i].id = i;
    	for(int i = 1; i <= n; i++) read(B[i].val), B[i].id = i;
    	sort(A + 1, A + n + 1, cmp);
    	sort(B + 1, B + n + 1, cmp);
    	for(int i = 1; i <= n; i++) posa[A[i].id] = posb[B[i].id] = i, Upd(i);
    	int x, y;
    	while(q--)
    	{
    		read(x), read(y), swap(B[posb[x]].id, B[posb[y]].id), swap(posb[x], posb[y]);
    		for(int i = -2; i <= 0; i++)
    			Upd(posa[x+i]), Upd(posb[x+i]), Upd(posa[y+i]), Upd(posb[y+i]);
    		dp[n+1] = 0;
    		for(int i = n; i; i--)
    		{
    			dp[i] = max(dp[i+2] + cost[i][0], dp[i+3] + cost[i][1]);//i与i+1/2不能配对的情况一定会被另外更优的情况所取代,可能是此行的另一个max,可能是下一行的单独配对时取的max
    			if(A[i].id != B[i].id) dp[i] = max(dp[i], dp[i+1] + 1ll * A[i].val * B[i].val);
    		}
    		printf("%lld
    ", dp[1]);
    	}
    }
    
  • 相关阅读:
    .NET Core MVC下的TagHelper
    测试.NET core MiddleWare 运行逻辑
    中台
    VSCode 完美整合前后端框架(angular2+.NET core)
    三分钟热度并非贬义
    【算法】莫队算法粗略讲解
    【题解】洋溢着希望
    【三角学】三角恒等变换公式推导
    【题解】方差
    【数据结构】FHQ Treap 详解
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039494.html
Copyright © 2020-2023  润新知