绵阳东辰国际test201910.24

分析:

考虑如何判断一个字符串 S 是不是另一个字符串 T 的子序列。
一个自然的想法是贪心:我们按照从前往后的顺序考虑 S 的每个字母,然后维护
一个 j,表示 S1S2 · · · Si 当前的字符已经匹配到了 T1T2 · · · Tj。然后每次贪心选择下
一个使得 Tj′ = Si+1 的 j′ 匹配过去的

我们考虑 DP 时记录两个序列对应的 j,

那么转移时,预处理出每个字符串每个位置后面第一个 0/1 在哪里即可做到 Θ(1) 转移

nxta[n+1][0]=nxta[n+1][1]=n+1;
	nxtb[m+1][0]=nxtb[m+1][1]=m+1;
	try(i,n,0)
		nxta[i][0]=(a[i+1]=='0'?i+1:nxta[i+1][0]),
		nxta[i][1]=(a[i+1]=='1'?i+1:nxta[i+1][1]);
	try(i,m,0)
		nxtb[i][0]=(b[i+1]=='0'?i+1:nxtb[i+1][0]),
		nxtb[i][1]=(b[i+1]=='1'?i+1:nxtb[i+1][1]);

两个序列是独立的

dp[i,j]------->dp[next[i,0]+1,next[j,0]+1];

--------->dp[next[i,1]+1,next[j,1]+1];

+1是为了失配

满足最短的前提是非子序列

所以要从前往后扫

又因为dp是线性的

所以可以在bfs上扫

所以最先到达终点状态的一定最短

又要求字典序最小,则在bfs中先处理0再处理1

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define enum(i,x,y) for(int i=(x);i<=(y);++i)
#define try(i,x,y) for(int i=(x);i>=(y);--i)
void readint(int &x)
{
	x=0;int f=1;char c;
	for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=x*10+c-'0';
	x*=f;
}
inline void chkmin(int &x,int y){x>y?x=y:0;}
inline void chkmax(int &x,int y){x<y?x=y:0;}
const int MAXN=4005,INF=0x3f3f3f3f;

int n,m;
char a[MAXN],b[MAXN];
int nxta[MAXN][2],nxtb[MAXN][2];
pii q[MAXN*MAXN],from[MAXN][MAXN];
bool vis[MAXN][MAXN];
char c[MAXN][MAXN];
char res[MAXN];

int main()
{
	#ifndef ONLINE_JUDGE
    freopen("code.in","r",stdin);
    //freopen("code.out","w",stdout);
	#endif
	readint(n);readint(m);
	scanf("%s%s",a+1,b+1);
	nxta[n+1][0]=nxta[n+1][1]=n+1;
	nxtb[m+1][0]=nxtb[m+1][1]=m+1;
	try(i,n,0)
		nxta[i][0]=(a[i+1]=='0'?i+1:nxta[i+1][0]),
		nxta[i][1]=(a[i+1]=='1'?i+1:nxta[i+1][1]);
	try(i,m,0)
		nxtb[i][0]=(b[i+1]=='0'?i+1:nxtb[i+1][0]),
		nxtb[i][1]=(b[i+1]=='1'?i+1:nxtb[i+1][1]);
	
	int front=1,rear=0;
	q[++rear]=mp(0,0);
	vis[0][0]=1;
	while(front<=rear)
	{
		pii p=q[front++];
		int tx=nxta[p.x][0],ty=nxtb[p.y][0];
		if(!vis[tx][ty])
		{
			vis[tx][ty]=1;
			q[++rear]=mp(tx,ty);
			from[tx][ty]=mp(p.x,p.y);
			c[tx][ty]='0';
		}
		tx=nxta[p.x][1],ty=nxtb[p.y][1];
		if(!vis[tx][ty])
		{
			vis[tx][ty]=1;
			q[++rear]=mp(tx,ty);
			from[tx][ty]=mp(p.x,p.y);
			c[tx][ty]='1';
		}
	}
/*enum(i,0,n+1)
{
	enum(j,0,m+1)
		cerr<<"("<<from[i][j].x<<","<<from[i][j].y<<") ";
	cerr<<endl;
}*/
	
	int cur=0;
	int x=n+1,y=m+1;
	while(x || y)
	{
		res[++cur]=c[x][y];
		pii p=from[x][y];
		x=p.x,y=p.y;
	}
	try(i,cur,1)putchar(res[i]);
	return 0;
}

分析:

考虑如何使f[x]最大化

每个出现的数次数为(2的k次方-1)

一定会有这样的最优解方案

why?

因为如果二进制下他不是这种形式,

即说明在这其中会有0

去掉0,f的值不会改变,但原数变小了

白嫖?何乐而不为呢?

code:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
template <typename T>inline T read()
{
    register T sum=0;
    register char cc=getchar();
    int sym=1;
    while(cc!='-'&&(cc>'9'||cc<'0'))cc=getchar();
    if(cc=='-')sym=-1,cc=getchar();
    sum=sum*10+cc-'0';
    cc=getchar();
    while(cc>='0'&&cc<='9')sum=sum*10+cc-'0',cc=getchar();
    return sym*sum;
}
template <typename T>inline T read(T &a)
{
    a=read<T>();
    return a;
}
template <typename T,typename... Others> inline void read(T& a, Others&... b)
{
    a=read(a);
	read(b...);
}
long long T,n,ans;
bool check(long long M)
{
	long long sum=0;
	ans=0;
	int i=0;
	for(long long j=1;M/j>=1;i++,j<<=1)
	{
		int r=M/j,l=M/(j<<1)+1;
		ans+=(i+1)*(r-l+1);
		sum+=((j<<1)-1)*(l+r)*(r-l+1)/2;
	}
	if(sum>=n)return false;
	ans+=(n-sum)/(M+1);
	return true;
}
int main()
{
	T=read<int>();
	for(int i=1;i<=T;i++)
	{
		n=read<long long>();
		long long l=1,r=1e9;
		while(l<=r)
		{
			long long mid=(l+r)>>1;
			if(check(mid))
				l=mid+1;
			else
				r=mid-1;
		}
		check(r);
		printf("%lld\n",ans);
	}
    return 0;
}

分析:

考虑从大到小依次放入元素,要形成合法的序列,加入的元素只能放在当前序列的最左边或者最右边.

加入这个元素时产生的贡献是一个类似于逆序对的东西,可以用树状数组求出加在左边,右边分别产生的贡献.

而加在左边还是右边对之后加入的元素的贡献是没有影响的,若后来的元素加在左边,则它总在后来的那个元素右边.

于是就贪心加,左右两边那边贡献更小,就加在哪边.

#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
using namespace std;
int t[300005],n;
void modify(int pos){for(int i=pos;i<=n;i+=lowbit(i)) t[i]++;}
int query(int pos){
    int res=0;
    for(int i=pos;i;i-=lowbit(i)) res+=t[i];
    return res;
}
vector<int>pos[300005];
int main(){
    scanf("%d",&n);
    for(int i=1,x;i<=n;i++) scanf("%d",&x),pos[x].push_back(i);
    long long ans=0;
    for(int i=n;i>0;i--){
        int l=pos[i].size();
        for(int j=0;j<l;j++) ans+=min(query(pos[i][j]-1),query(n)-query(pos[i][j]));
        for(int j=0;j<l;j++) modify(pos[i][j]);
    }
    cout<<ans;
    return 0;
}
posted @ 2019-10-24 21:59  wzx_believer  阅读(122)  评论(0编辑  收藏  举报