zoukankan      html  css  js  c++  java
  • 3.27模拟赛

    (挂惨了 我晚上一定好好睡觉。

    T1 求冒泡排列在第几轮结束。(nleq 3e7)

    可以发现每次自己左边最大的数字是向右移动的 所以答案为max{自己左边有多少个数比自己大}

    想要O(n)求出的话还要再考虑一下 如何求出max 由于是max 我们考虑自己右边最多有多少比自己小的max等价于原答案。

    所以我们可以发现 利用较小的数字来统计答案会比用较大的数字统计答案更优 换个说法 (a_i<a_j) (a_i)(a_j)如果进行换位了 那么我们只统计(a_i)的对答案的

    贡献至少不会比(a_j)要小。一个很难得到最很好好证明的答案:max{i-(a_i)}即为答案。

    考虑证明:一个数字前面包括本身最多有(a_i)个数对答案没有贡献。考虑如果不够这(a_i)个数 那么一定有比自己还要小的数字在自己后面 那么这样统计下去至少能够找到答案。

    bf:
    const int MAXN=30000010;
    int n;
    ll S,B,C,D;
    int a[MAXN],c[MAXN];
    int cnt,ans;
    int main()
    {
    	freopen("bubble.in","r",stdin);
    	freopen("bubble.out","w",stdout);
    	get(n);get(S);get(B);get(C);get(D);
    	rep(1,n,i)
    	{
    		a[i]=i;
    		S=(S*B+C)%D;
    		swap(a[i],a[(S%i)+1]);
    	}
    	//rep(1,n,i)cout<<a[i]<<' '<<endl;
    	rep(1,n,i)
    	{
    		int w=a[i];cnt=0;
    		while(w)
    		{
    			cnt+=c[w];
    			w-=w&(-w);
    		}
    		ans=max(ans,i-1-cnt);
    		w=a[i];
    		while(w<=n)
    		{
    			++c[w];
    			w+=w&(-w);
    		}
    	}
    	put(ans);
    	return 0;
    }
    sol:
    
    const int MAXN=30000010;
    int n;
    ll S,B,C,D;
    int a[MAXN],c[MAXN];
    int cnt,ans;
    int main()
    {
    	freopen("1.in","r",stdin);
    	get(n);get(S);get(B);get(C);get(D);
    	rep(1,n,i)
    	{
    		a[i]=i;
    		S=(S*B+C)%D;
    		swap(a[i],a[(S%i)+1]);
    	}
    	//rep(1,n,i)cout<<a[i]<<' '<<endl;
    	rep(1,n,i)ans=max(ans,i-a[i]);
    	put(ans);
    	return 0;
    }
    

    T2:

    avatar
    avatar

    sb匹配题...无语。(每次我都暴力 每次暴力还挂。我真是个菜鸡。

    容易推出性质 两个匹配的字符串a,b a种某种字符只能对应b上的位置只能对应一种字符 b种也同理 容易证明这样是可以匹配的充要条件。

    可以简单证明一下:如果满足上述条件 那么直接swap即可 可以发现这样做就可以形成一模一样的字符。考虑必要条件的证明:反证:如果不满足上述情况 那么必然某种字符对应了两种字符 而swap每次只能将一种字符变成另外一种字符 如果swap是那两种字符 显然无法匹配还是原来的情况 如果不是 显然还是原情况。证毕。

    所以现在问题变成了 给出两个字符串看 是否满足上述条件。考虑暴力 n^2 匹配。最尴尬的事实是优化不了 这个思路虽然没错 但是 必须一一得到位置信息。

    贴一发暴力代码:

    const int MAXN=1000010;
    int T,C,n,m,top;
    int ans[MAXN];
    int a[MAXN],b[MAXN],c[MAXN],c1[MAXN],cnt,cnt1;
    vector<int>w[MAXN];
    int main()
    {
    	freopen("1.in","r",stdin);
    	//freopen("match.out","w",stdout);
    	get(T);get(C);
    	while(T--)
    	{
    		get(n);get(m);top=0;
    		rep(1,C,i)w[i].clear();
    		rep(1,n,i)get(a[i]);
    		rep(1,m,i)get(b[i]),w[b[i]].pb(i);
    		rep(1,n-m+1,i)
    		{
    			int cnt=0;
    			rep(1,C,j)
    			{
    				if(w[j].size())
    				{
    					int last=0;
    					rep(0,w[j].size()-1,k)
    					{
    						int ww=w[j][k];
    						if(last&&last!=a[i+ww-1]){cnt=1;break;}
    						last=a[i+ww-1];
    						if(c[last]&&c[last]!=j){cnt=1;break;}
    						c[last]=j;
    					}
    					if(cnt)break;
    				}
    			}
    			rep(1,C,j)c[j]=0;
    			if(!cnt)ans[++top]=i;
    		}
    		put(top);
    		rep(1,top,i)printf("%d ",ans[i]);
    		puts("");
    	}
    	return 0;
    }
    

    可以发现这是一个sb匹配题 我们可以考虑算法了 hash 或者 KMP(关于自己的子串和模式串的匹配问题。

    hash不太会 但是考虑KMP的话 我们可以通过性质定义一种匹配 自己上次的字符的匹配位置和当前位置的差 恰好等于自己匹配到的字符的上次位置和这次位置的差值。如果完全符合的话显然和我们的性质是刚好吻合的。

    考虑KMP做这件事 接下来是魔改KMP...(这题的暴力分给的很不合理 推出性质之后暴力只有10分 正解KMP想出来了才能拿100.果然算法要套到题目上 这个角度来思考这道题更容易出解 而不是得到性质之后套到暴力之上。

    建立数组 la[i] 表示 a串的i的上一个位置和当前位置的差值 lb[i]同理 进行匹配即可。

    值得注意的是 存在断层现象 可能当前匹配的长度为 w w+1上一次出现的位置与w+1的差是大于w的 此时默认为w+1上的字符为第一次出现 那么值为w+1即可。

    剩下的就没有了。复杂度O(n)

    
    const int MAXN=1000010;
    int T,C,n,m,top;
    int ans[MAXN];
    int a[MAXN],b[MAXN],c[MAXN],la[MAXN],lb[MAXN];
    int nex[MAXN];
    inline void KMP()
    {
    	int j=0;top=0;
    	rep(2,m,i)
    	{
    		while(min(j+1,lb[i])!=lb[j+1]&&j)j=nex[j];
    		if(min(j+1,lb[i])==lb[j+1])++j;
    		nex[i]=j;
    	}
    	j=0;
    	rep(1,n,i)
    	{
    		while(min(j+1,la[i])!=lb[j+1])j=nex[j];
    		if(min(j+1,la[i])==lb[j+1])++j;
    		if(j==m)j=nex[j],ans[++top]=i;
    	}
    }
    int main()
    {
    	freopen("1.in","r",stdin);
    	//freopen("match.out","w",stdout);
    	get(T);get(C);
    	while(T--)
    	{
    		get(n);get(m);
    		rep(1,C,i)c[i]=0;
    		rep(1,n,i)
    		{
    			get(a[i]);
    			la[i]=i-c[a[i]];
    			c[a[i]]=i;
    		}
    		rep(1,C,i)c[i]=0;
    		rep(1,m,i)
    		{
    			get(b[i]);
    			lb[i]=i-c[b[i]];
    			c[b[i]]=i;
    		}
    		KMP();
    		put(top);
    		rep(1,top,i)printf("%d ",ans[i]-m+1);
    		puts("");
    	}
    	return 0;
    }
    

    T3:
    avatar
    avatar

    一道没见过的dp优化题目。
    看出来dp之后可以列状态f[i]表示到达第i页的最大价值。
    页数1e9当场gg 很容易发现和页数无关 之和我们到达第i个关键点了没有有关。

    考虑最优策略 一定是从第K页一路上经过若干个关键点过来的。f[i]表示到达第i个点的最大价值。

    容易得到转移 (f[i]=f[j]-lceilfrac{(T_i-T_j)}{D} ceilcdot A+B_i)

    转移是(n^2)的 暂时看不出来怎么优化。

    不过这个向上取整的式子我倒是可以拆成向下取整。

    (lceilfrac{a}{b} ceil=frac{a-1}{b}+1)

    没啥用。。。

    不过这倒是可以进一步化简这个式子。

    (lfloorfrac{a-c}{b} floor=frac{a}{b}-frac{c}{b})-(a%b<c%b)

    到这里可以发现 还是没有任何用 我只不过在玩这个式子罢了/cy 至少对写暴力有很大的优化...

    bf score:20
    
    const ll MAXN=100010;
    ll f[MAXN];
    ll K,M,D,A,n,maxx=-INF;
    ll a[MAXN],b[MAXN];
    signed main()
    {
    	freopen("1.in","r",stdin);
    	//freopen("reading.out","w",stdout);
    	f[0]=0;get(K);get(M);get(D);get(A);get(n);
    	rep(1,n,i)get(a[i]),get(b[i]),f[i]=-INF;a[0]=K;
    	//if(n<=1000)
    	{
    		rep(1,n,i)
    		{
    			rep(0,i-1,j)
    			{
    				ll w=(a[i]-a[j]-1)/D+1;
    				f[i]=max(f[i],f[j]+b[i]-w*A);
    			}
    		}
    		rep(0,n,i)
    		{
    			ll w=(M-a[i]-1)/D+1;
    			f[i]=f[i]-w*A;
    			maxx=max(maxx,f[i]);
    		}
    		putl(maxx);
    	}
    	return 0;
    }
    

    考虑题目中的子任务 D<=100

    观察我们的dp式如果存在 j<k 且j和k 模D同余 我们从dp的角度分析从j转移过来没啥用 直接从k转移即可。

    这样我们得到了一个非常好些的 nD的dp.

    接下来就要让这两个方法结合 其实光我第一个dp就可以推出正解。

    考虑dp式的化简 (f[i]=f[j]-lceilfrac{(T_i-T_j)}{D} ceilcdot A+B_i) 这个向上取整等价于 (frac{T_i}{D}-frac{T_j}{D})+((T_i)%D>(T_j)%D.

    绝杀化简 对于任意一条路径来说 对于路径上两个相连的点 i j 他们对答案的贡献的(frac{T_i}{D}-frac{T_j}{D})的连续累加 恰好等于(frac{M}{D}-frac{K}{D}).

    也就是说我们只需要管 后面那个东西 就行了 这其实是是否多了一个A的关系 采用数据结构优化一下即可。

    区间查询最值 考虑线段树 (zkw线段树常数会很小很小。或者标记永久化 没必要懒标记了。这里推荐zkw.

    考虑一下对于绝杀定理的证明 通过感性理解 严格观察 我可以发现是正确的 而并非证明/ll.

    学了一发 zkw线段树 把这题A了 不是很想写普通线段树了...

    
    const ll MAXN=100010;
    ll f[MAXN];
    ll K,M,D,A,n,maxx=-INF,top,num,base;
    ll a[MAXN],b[MAXN],c[MAXN],val[MAXN<<2];
    inline void discrete()
    {
    	sort(c+1,c+1+num);
    	rep(1,num,i)if(i==1||c[i]!=c[i-1])c[++top]=c[i];
    	rep(0,n,i)a[i]=lower_bound(c+1,c+1+top,a[i])-c;
    }
    inline void build(ll len)
    {
    	while(base<=len)base=base<<1;
    	rep(1,base*2,i)val[i]=-INF;
    }
    inline void modify(ll pos,ll x)
    {
    	pos+=base;val[pos]=max(val[pos],x);pos=pos>>1;
    	while(pos)
    	{
    		val[pos]=max(val[pos<<1],val[pos<<1|1]);
    		pos=pos>>1;
    	}
    }
    inline ll ask(ll l,ll r)
    {
    	ll res=-INF;
    	for(l=base+l-1,r=base+r+1;l^r^1;l=l>>1,r=r>>1)
    	{
    		if(~l&1)res=max(res,val[l^1]);//经过左儿子
    		if(r&1)res=max(res,val[r^1]);//经过右儿子
    	}
    	return res;
    }
    signed main()
    {
    	freopen("1.in","r",stdin);
    	//freopen("reading.out","w",stdout);
    	get(K);get(M);get(D);get(A);get(n);
    	rep(1,n,i)get(a[i]),get(b[i]),a[i]%=D,c[i]=a[i];
    	num=++n;a[n]=M%D;c[n]=a[n];c[++num]=a[0]=K%D;discrete();
    	base=1;build(top);
    	modify(a[0],0);
    	rep(1,n,i)
    	{
    		ll w1=ask(1,a[i]-1)-A;
    		ll w2=ask(a[i],top);
    		f[i]=max(w1,w2)+b[i];
    		modify(a[i],f[i]);
    	}
    	putl(f[n]-(M/D-K/D)*A);
    	return 0;
    }
    

    脑子还是不够灵光 要努力猜结论 猜算法 将性质和算法联系在一起。

  • 相关阅读:
    if控制器+循环控制器+计数器,控制接口分支
    前置处理器
    逻辑控制器
    配置元件
    基础元件
    docker etcdctl报错:etcdctl No help topic for 'put'
    celery定时执行ansible api返回为空的问题
    Ansible+Jenkins+Gitlab搭建及配置
    进击的Python【第十六章】:Web前端基础之jQuery
    进击的Python【第十五章】:Web前端基础之DOM
  • 原文地址:https://www.cnblogs.com/chdy/p/12582022.html
Copyright © 2011-2022 走看看