zoukankan      html  css  js  c++  java
  • 省选模拟测试14

    (T1) 题意理解错了,暴力分就打挂了。

    (T2) 打了个 (O(n^2)) 的暴力,一些边界问题没注意到就写错了。

    (T3) 看题面以为是个组合计数的题,和题解的思路一点也不一样。

    T1 String

    题意描述

    你有一个字符串 (S_0),想要用它造出共 (n) 个新字符串 (S_{0},cdots,S_{n-1})

    你会按顺序进行 (n-1) 次操作,造出新字符串。第 (i) 次操作有两种方法:

    • S x l r 截取 (S_x)([l,r)) 子串,即第 (l) 到第 (r-1) 个字符作为 (S_i)
    • A x y(S_x)(S_y) 拼接作为 (S_i)

    最后,你会关心 (S_{n-1}) 长什么样,因此请输出 (S_{n-1}) 所有字符的 ASCII 码之和,结果对 (10^9+7) 取模。

    本题中字符串下标均从 (0) 开始编号,且保证所有字符串串长不超过 (2^{63}-1),并且所有操作均合法(即 (0leq x,y<i,0leq l < rleq Len(S_x))

    数据范围:(nleq 2000,len(S)leq 2000)

    solution

    记忆化搜索。

    暴力递归能有 (60pts), 加上记忆化搜索就过了。

    注意字符串长度要开 ( unsigned long long)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<map>
    using namespace std;
    #define int long long
    #define mp make_pair
    const int p = 1e9+7;
    const int N = 5010;
    int n,len[N],num[N],sum[N];
    string s[N];
    struct node
    {
        int opt,x,l,r;
    }q[N];
    struct QAQ
    {
    	int x,l,r;
    	QAQ(){}
    	QAQ(int a,int b,int c){x = a,l = b, r = c;}
    };
    bool operator < (QAQ a,QAQ b)
    {
    	if(a.x == b.x) 
    	{
    		if(a.l == b.l) return a.r < b.r;
    		else return a.l < b.l;
    	}
    	return a.x < b.x;
    }
    map<QAQ,int> has;
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    int dfs(int now,int l,int r)
    {
        if(has[QAQ(now,l,r)]) return has[QAQ(now,l,r)];
        if(now == 1)
        {
        	if(l == 0) return has[QAQ(now,l,r)] = sum[r];
        	else return has[QAQ(now,l,r)] = sum[r]-sum[l-1];
        }
        if(q[now].opt == 1) 
        {
            int to = q[now].x;
            return has[QAQ(now,l,r)] = dfs(to,q[now].l+l,q[now].l+r);
        }
        if(q[now].opt == 2)
        {
            int son1 = q[now].l;
            int son2 = q[now].r;
            if(r < len[son1]) return has[QAQ(now,l,r)] = dfs(son1,l,r);
            else if(l >= len[son1]) return has[QAQ(now,l,r)] = dfs(son2,l-len[son1],r-len[son1]);
            else return has[QAQ(now,l,r)] = dfs(son1,l,len[son1]-1) + dfs(son2,0,r-len[son1]); 
        }
    }
    signed main()
    {
    //	freopen("string.in","r",stdin);
    //	freopen("string.out","w",stdout);
        n = read(); cin>>s[1];
        len[1] = s[1].length(); sum[0] = (int) s[1][0]; 
        for(int i = 1; i < len[1]; i++) sum[i] = sum[i-1] + (int)s[1][i];
        for(int i = 2; i <= n; i++)
        {
            char ch; cin>>ch;
            if(ch == 'S')
            {
                q[i].opt = 1;
                q[i].x = read() + 1;
                q[i].l = read();
                q[i].r = read();
                len[i] = (q[i].r-q[i].l);
            }
            else
            {
                q[i].opt = 2;
                q[i].l = read() + 1;
                q[i].r = read() + 1;
                len[i] = len[q[i].l] + len[q[i].r];
            }
        }
        printf("%lld
    ",dfs(n,0,len[n]-1));
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T2 Different

    题意描述

    你有 (n) 株植物,高度为 (a_i)。你希望让它们的高度看起来不太相同,具体来说:任意两株高度差的绝对值不小于 (d)

    每次你可以选择一株植物,将其拔高或压低 (1),这花费你 (1) 的力气,这个操作可以进行任意多次。注意,植物高度不能是负的,即你需要保证任意时刻 (a_igeq 0)

    你最少需要使用多少力气,才能使得植物高度看起来不太相同呢?

    数据范围:(sum nleq 3cdot 10^5,1leq dleq 10^6, 0leq a_ileq 10^{11})

    solution

    动态规划。

    首先我们先排一下序,令 (a_i = a_i-(i-1) imes d)

    那么我们的问题就转化为最小代价使序列单调递增。

    (f_{i,j}) 表示处理完前 (i) 柱植物且第 (i) 柱植物的高度为 (j) 的最小代价是多少。

    转移则有:(f_{i,j} = min(f_{i-1,k} + |a_i-j|) kin{{- infty,j}})

    直接转移是 (O(n^2)) 的。

    考虑维护一下这个函数的图像,不难发现每次转移都是取前缀最大值在加一个平移的绝对值函数。

    由于斜率连续,所以我们考虑那堆来维护一下拐点。

    处理的时候要特判一下 (a_i<0) 以及 (a_i = 0) 的情况。

    最后根据斜率就可以算出 (f(0)) 的值。

    Code

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #define int long long
    using namespace std;
    const int N=3e5+7;
    int T,n,d,cnt,res;
    int a[N],s[N];
    priority_queue<int> q;
    signed main()
    {
        scanf("%lld",&T);
        while(T--)
        {
    		res=0;
    		scanf("%lld%lld",&n,&d);
    		for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
    		sort(a+1,a+n+1);
    		for(int i = 1; i <= n; i++)
    		{
    			int x = max(0ll,a[i]-(i-1)*d);
    			q.push(x); q.push(x); q.pop();
    		}
    		for(int i = 1; i <= n; i++) res += abs(a[i]-(i-1)*d);
    		cnt=0;
    		while(q.size()) s[++cnt] = q.top(),q.pop(); s[++cnt]=0;
    		for(int i = 1; i < cnt; i++) res -= i * (s[i]-s[i+1]);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    } 
    

    T3 Circle

    题意描述

    你有 (2n+m) 根绳子,每根绳子两端染有黑、白两种颜色。

    其中 (n) 根绳子两端都是黑色; (n) 根绳子两端都是白色; (m) 根绳子一段黑色另一端白色。

    现在绳子一端为黑色和白色的线头分别有 (2n+m) 个,你想要把黑色和白色一端的线头随机配对(共 ((2n+m)!) 种方法),每对配对的线头用胶水拼接起来。可以发现,这样所有绳子一定会绕成若干个环。

    你想要求出环的个数期望是多少,结果对 (10^9+7) 取模。

    数据范围: (n,mleq 10^6)

    solution

    期望 (dp) 。这题的思路好奇妙啊。

    考虑设 (f[i][j]) 表示有 (i) 个绳子两端都是黑色 (i) 根两端都为白色,(j) 根绳子一端黑色一端白色的环的个数的期望。

    转移就有:(f[n][m] = f[n][m-1] + {1over 2n+m})

    解释一下,考虑取出一条一端为黑色一端为白色的线段去和 (2n+m) 条线段匹配。

    为了画图方便(偷懒)图上的红色表示端点为黑色,蓝色则表示端点为白色。

    不难发现只有这条线段和自己匹配的时候环的数量才会加一。其余情况和图上画的那个是一样的。

    因为我们取出来了一条一段为黑色一段为白色的线段,所以剩下的两种线段的个数为分别为 (n,m-1).

    在乘上概率就得到了我们的递推式 (f[n][m] = f[n][m-1] + {1over 2n+m})

    现在只要知道 (f[n][0]) 我们就可以递推的求出 (f[n][m]) 了。

    对于 (f[n][0]) 又转移 (f[n][0] = f[n-1][1] = f[n-1][0] + {1over 2n-1})

    考虑拿出一条两端都为黑色的线段去和其他线段匹配,显然这条线段只能和两端都为白色的匹配,从而得到一端为白色一端为黑色的线段。匹配完之后两种线段的数量分别为:(n-1,1) 。又因为在这个匹配的过程中环的数量不会增加,所以就有 (f[n][0] = f[n-1][1])

    化简一下就可以得到:(f[n][0] = f[n-1][0] + {1over 2n-1})

    有了这两个递推式,我们就可以在 (O(n+m)) 的时间内递推出 (f[n][m]) 了。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    const int N = 3e6+10;
    const int p = 1e9+7;
    int n,m,ans,f[N],g[N];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    int ksm(int a,int b)
    {
    	int res = 1;
    	for(; b; b >>= 1)
    	{
    		if(b & 1) res = res * a % p;
    		a = a * a % p;
    	}
    	return res;
    }
    signed main()
    {
    	freopen("circle.in","r",stdin);
    	freopen("circle.out","w",stdout);
    	n = read(); m = read();
    	for(int i = 1; i <= n; i++) f[i] = (f[i-1] + ksm(2*i-1,p-2)) % p;
    	g[0] = f[n];
    	for(int i = 1; i <= m; i++) g[i] = (g[i-1] + ksm(2*n+i,p-2)) % p;
    	printf("%lld
    ",g[m]);
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    jQuery遍历之siblings()
    命名空间
    AnsiString
    《linux内核完全注释》读书笔记 2
    mciSendString
    PeekMessage, GetMessage, PostMessage, SendMessage
    c/c++中运行外部程序或打开文件(转)
    定义基类和派生类
    复制构造函数
    让StringGrid控件显示下拉菜单
  • 原文地址:https://www.cnblogs.com/genshy/p/14577205.html
Copyright © 2011-2022 走看看