cxsys加训20201119

题目链接: 戳这里~

垃圾小菜鸡终于补完了!虽然防AK的题和一道网络流的题没有做,其他终于是搞明白了。
知识点一定要补足!菜鸡小李冲!

A:二分+差分数组
题意:
一个人在养花,初始高度为a[1–n] 。我每天可以给连续k朵花浇水,浇一次长高1.问m天后最矮的花最高多么高。

解析:
可以利用二分的思想来做,二分这个想法,真的好常用啊,以后做题,一旦没有好思路,先问问自己二分行不行! 在判断的时候,利用差分数组来做。注意初始高度都相同这种情况

题解:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
typedef long long ll;
using namespace std;
const int N=1e5+5;

int n,m,w;
ll a[N]={0};
bool ok(ll x)
{
	ll d[N]={0};
	for(int i=1;i<=n;i++)
	{
		d[i]=a[i]-a[i-1];	
	}
	ll tall=0;
	int water=0;
	for(int i=1;i<=n;i++)
	{
		tall+=d[i];
		if(tall<x)//bugou
		{
			ll need=abs(x-tall);
			water+=need;
			d[i]+=need;
			int rr=i+w>n?n+1:i+w;
			d[rr]-=need;
			tall=x;
			if(water>m)
			return false;
		}
	}
//	for(int i=1;i<=n;i++)
//	cout<<i<<"  "<<d[i]<<endl;
	if(water>m)
	return false;
	ll sum=0;
//	bool flag=false;
//	for(int i=1;i<=n;i++)
//	{
//		sum+=d[i];
		if(sum==x)
		flag=true;
cout<<i<<"  "<<sum<<endl;
//	}
	return true;
}
int main()
{
	scanf("%d %d %d",&n,&m,&w);
	ll minn=1e9+9;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		minn=min(minn,a[i]);
	}
	ll l=minn,r=minn+m;
	while(l<=r)
	{
		ll mid=(l+r)/2;
		if(ok(mid))
		{
			l=mid+1;
		}
		else
		{
			r=mid-1;
		}
//		cout<<mid<<"-----"<<ok(mid)<<endl;
	}
	cout<<l-1<<endl;;
	
}

B:二分+ST
题意:
n个机器人,有m个属性,每个机器人的属性值a1-am已经给出,我有一把枪,也有m个属性,若把枪调为第i种属性,每打一枪可以让这n个机器人的属性i减1。当一个机器人的所有属性值都<=0时,该机器人死亡。我现在可以打k枪,问怎么打能让连续的死亡的机器人的长度最大。输出为属性为1-m,每种属性打几枪。可以不用打满k枪。

分析:
二分连续死亡的值,判定使用ST,nlogn的预处理,O(1)查询区间最值。
ST利用倍增的思想,在nlogn的预处理后,可以在O(1)的时间内查询最值

题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Inf 0x3f3f3f3f
#define PI  acos(-1.0)
using namespace std;
typedef long long ll;
const double eps = 1e-6;
const int N = 1e5+5;
const int mod=1e9+7;
ll n,m,k;
ll a[N][6];
ll ans[6];
ll f[N][20][6];
void ST_prework()
{
	for(int k=1;k<=m;k++)
	{
		for(int i=1;i<=n;i++)
		f[i][0][k]=a[i][k];
		int t=log(n)/log(2)+1;
		for(int j=1;j<t;j++)
		{
			for(int i=1;i<=n-(1<<j)+1;i++)
			{
				f[i][j][k]=max(f[i][j-1][k],f[i+(1<<(j-1))][j-1][k]);
			}
		}
	}
}
int ST_query(int l,int r,int k)
{
	int j=log(r-l+1)/log(2);
	return max(f[l][j][k],f[r-(1<<j)+1][j][k]);
}
bool ok(ll x)
{	
	for(int s=1;s+x-1<=n;s++)
	{
		ll e=s+x-1;
		ll last=k;
		for(int j=1;j<=m;j++)
		{
			last-=ST_query(s,e,j);
		}
		if(last>=0)
		return true;
	}
	return false;	
	  
}
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	scanf("%lld %lld %lld",&n,&m,&k);
	for(int i=1;i<=n;i++)
	{
		a[i][0]=0;
		for(int j=1;j<=m;j++)
		{
			scanf("%lld",&a[i][j]);
		}
	}
	ST_prework();
	ll l,r;
	l=0;r=n;
	while(l<=r)
	{
		ll mid=(l+r)/2;
		if(ok(mid))
		l=mid+1;
		else
		r=mid-1;
	}
	l-=1; 
	for(int s=1;s+l-1<=n;s++)
	{
		ll e=s+l-1;
		ll last=k;
		for(int j=1;j<=m;j++)
		{
			last-=ST_query(s,e,j);
			ans[j]=ST_query(s,e,j);
		}
		if(last>=0)
		break;
	}
//	cout<<l<<endl;
	for(int i=1;i<=m;i++)
	printf("%lld%c",ans[i],i==m?'\n':' ');
    return 0;
}

C:set的应用
题意:
给定n个数a[1-n],在给一个数m,n%m==0,要求用最少的加1次数使得a[1]-a[n]取模m后,结果为0-m-1的个数都相同。输出变化后的数组
分析:
一开始不太会,看了题解明白了。
设置一个set,里面存放了所有,个数没到n/m的取模结果(0-m-1)一开始所有数都在里面。然后,依次看每个a[i]取模的结果,如果结果在set中,证明他还没到n/m,那就另这个结果加一,如果个数等于n/m了就把它从set中移走,如果这个结果不在set中,那证明他已经满足了,那就加到离他最近的未满足的结果中,在进行更改。

题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Inf 0x3f3f3f3f
#define PI  acos(-1.0)
using namespace std;
typedef long long ll;
const double eps = 1e-6;
const int N = 2e5+5;
const int mod=1e9+7;
#define int long long
int n;
int m;
int a[N];
int c[N]={0};
set<int> s;
set<int>::iterator it;
int change[N]={0};
ll ans=0;
signed main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=0;i<=m-1;i++)
	{
		s.insert(i);		
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		int x=a[i]%m;
		int st;
		if(x>*s.rbegin())
			st=*s.begin();
		else
			st=*s.lower_bound(x);
		c[st]++;
		a[i]+=(st>=x?st-x:st+m-x)%m;
		ans+=(st>=x?st-x:st+m-x)%m;
//		a[i]+=(st-x+m)%m;
//		ans+=(st-x+m)%m;
		if(c[st]==n/m)
		s.erase(st);
		
	}
	printf("%lld\n",ans);
	for(int i=1;i<=n;i++)
	{
		printf("%lld%c",a[i],i==n?'\n':' ');
	}
	
    return 0;
}

D:网络流
不会网络流┭┮﹏┭┮,等着填坑吧

E:威尔逊定理+素数筛

题意:给定一个大素数P,要求计算他前一个素数Q,输出Q!%P

分析:
威尔逊定理:
((P-1)!+1) %P=0;
(P-1)! = -1(modP)
(p-2)!%p = 1

所以,我们可以先暴力枚举找出Q,可以利用威尔逊定理,然后利用威尔逊定理,计算(P-1)!/(Q+1-P-1) ,输出结果

题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<time.h>
#include<queue>
#define Inf 0x3f3f3f3f
#define PI  acos(-1.0)
using namespace std;
typedef long long ll;
const double eps = 1e-6;
const int N = 1e5+5;
const int mod=1e9+7;
ll add_mod(ll a,ll b,ll mod){    //快乘法 基于快速幂的二分思想 
    ll ans=0;                    //由于考虑到取模数很大 快速幂会溢出 
    while(b){                    //必须使用该方法 
        if(b&1)                    //我这里写的是非递归版 
            ans=(ans+a)%mod;
        a=a*2%mod;
        b>>=1;
    }
    return ans;
}

ll quick(ll a,ll b,ll mod){
	if(b==0) return 1;
	ll res=1;
	while(b!=1){
		if(b&1) res=add_mod(res,a,mod);//res=res*a%mod;
		a=add_mod(a,a,mod);//a=a*a%mod;
		b/=2;
	}
	return add_mod(a,res,mod);//res*a%mod;
}
bool Miller_Rabbin(ll n,ll a){//米勒拉宾素数判断函数主体
    ll d=n-1,s=0,i;    
    while(!(d&1)){            // 先把(2^s)*d 算出来 
        d>>=1;
        s++;
    }
    ll t=quick(a,d,n);    //a^d取一次余判断 
    if(t==1 || t==-1)        //一或负一则可以声明这可能是质数 
        return 1;
    for(i=0;i<s;i++){                //不是的话继续乘上s个2 
        if(t==n-1)            //(n-1)*(n-1)%n=1 这一步是优化 
            return 1;
        t=add_mod(t,t,n);    // 快乘 
    }
    return 0;
}

bool is_prime(ll n){
    ll i,tab[4]={3,4,7,11};//本来应该取[1,n]内任意整数 
    for(i=0;i<4;i++){                //但一般这几个数足以,不需要太多组测试 
        if(n==tab[i])
            return 1;        //小判断小优化~ 
        if(!n%tab[i])
            return 0;
        if(n>tab[i] && !Miller_Rabbin(n,tab[i]))
            return 0;
    }
    return 1;
}
ll quick_mul(ll a,ll b,ll mod)
{
	return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;
}
ll calu(ll P,ll Q)
{
	ll ans=P-1;
	for(ll i=P-1;i>=Q+1;i--)
	{
		ans=quick_mul(ans,quick(i,P-2,P),P);
	}
	return ans;
}
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;
	cin>>t;
	srand(time(NULL));
	while(t--){
		ll n;
		cin>>n;
		ll q;
		for(ll i=n-2;i>=2;i-=2){
			if(is_prime(i))
			{
				q=i;
				break;
			}
		}
//		cout<<q<<":::::"<<endl; 
		ll ans=calu(n,q);
		cout<<ans<<endl;
	}
    return 0;
}

F:0/1分数规划

题意,给定a[1-n],b[1-n],对每一个i,我可以选或者不选,目标最大化在这里插入图片描述
分析:
0/1规划模板题,利用二分的思想
需要补一下,最优比率树和最优比率环?
题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Inf 0x3f3f3f3f
#define PI  acos(-1.0)
using namespace std;
typedef long long ll;
const double eps = 1e-6;
const int N = 1e3+5;
const int mod=1e9+7;
int n,k;
ll a[N];
ll b[N];
bool calu(double x){
	double ret=0;
	double s[N]={0};
	for(int i=1;i<=n;i++)
	{
		s[i]=(a[i]-x*b[i]);
	}
	sort(s+1,s+1+n); 
	for(int i=k+1;i<=n;i++)
	ret+=s[i];
	return ret>=0;
}
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	while(cin>>n>>k){
		if(n==0&&k==0)
		break; 
		for(int i=1;i<=n;i++)
		cin>>a[i];
		for(int i=1;i<=n;i++)
		cin>>b[i];
		double l=0.0;
		double r=1.0;
		while(r-l>eps)
		{
			double mid=(l+r)/2;
			if(calu(mid)){
				l=mid;
			}else{
				r=mid;
			}
		}
		double ans=100*l;
//		cout<<ans<<endl;
		printf("%.0lf\n",ans);
	}
	
    return 0;
}

G:Floyd变形
题意:
给定n个点的一张图,每个点都有一个危险值,有q个询问。每次询问从起点st到终点ed的,经过所有点危险值不超过k的最短路径。
分析:
考察Floyd的含义,floyd的三层循环,第一层一定是k,外层表明,在前k个点的参与下,可以形成的最短路。所以,对于这个题而言,我们可以对点的危险值进行排序,按照危险值从小到大进行更新,然后在进行询问就可。
题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Inf 0x3f3f3f3f
#define PI  acos(-1.0)
using namespace std;
typedef long long ll;
const double eps = 1e-6;
const int N = 205;
const int mod=1e9+7;
int n,q;
struct node{
	int r;
	int pos;
}a[N];
bool cmp(node a,node b)
{
	return a.r<b.r;
}
int dp[N][N][N];
void ini(){
	memset(dp,Inf,sizeof(dp));
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	dp[i][i][j]=0;
	a[0].r=0;
}
void floyd(){
	
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				dp[i][j][k]=min(dp[i][j][k-1],dp[i][a[k].pos][k-1]+dp[a[k].pos][j][k-1]);
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;
	cin>>t;
	for(int ca=1;ca<=t;ca++)
	{
		cin>>n>>q;
		ini();
		for(int i=1;i<=n;i++)
		{
			cin>>a[i].r;
			a[i].pos=i;
		}
		sort(a+1,a+1+n,cmp);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				int x;
				cin>>x;
				dp[i][j][0]=dp[j][i][0]=x;
			}
		}
		floyd();
		printf("Case #%d:\n",ca); 
		while(q--){
			int u,v,w;
			cin>>u>>v>>w;
			int x;
			int ans=Inf;
			for(int i=0;i<=n;i++)
			{
//				if(!(a[i].pos==u||a[i].pos==v))
//					sum+=a[i].r;
//				if(sum<=w)
//					ans=min(dp[u][v][i],ans);
//				else
//					break;
				if(a[i].r<=w)
				x=i;
				else 
				break;
			}
			cout<<dp[u][v][x]<<endl;
		}
	} 
    return 0;
}

H:Dijstra算法
题意:
给定一张有向图,求从起点到终点的最短路,我可以缩短路径上一条边为原来一半。
分析:
经典题,双向Dijstra,枚举要缩短的边,计算最短路。
注意,邻接表存图一定注意更新next
题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Inf 0x3f3f3f3f3f3f
#define PI  acos(-1.0)
#define int long long
using namespace std;
typedef long long ll;
const double eps = 1e-6;
const int N = 1e5+5;
const int M=5e5+2000;
const int mod=1e9+7;
const int inf= 1e12;
int n,m,tot,tot2,cnt;
map<string,int> mp;
char s1[20],s2[20];
int l;
int head[M],ver[M],edge[M],Next[M],d[N];
int head2[M],ver2[M],edge2[M],Next2[M],D[N];
bool v[M];
inline void add(int x,int y,int z)
{
	ver[++tot]=y;
	edge[tot]=z;
	Next[tot]=head[x];
	head[x]=tot;
}
inline void add2(int x,int y,int z)
{
	ver2[++tot2]=y;
	edge2[tot2]=z;
	Next2[tot2]=head2[x];
	head2[x]=tot2;
}
priority_queue< pair<int,int> > q;
inline void Dijstra(int s){
	for(int i=1;i<=n;i++)
	d[i]=inf,v[i]=0;
	d[s]=0;
	q.push(make_pair(0,s));
	while(!q.empty())
	{
		int x=q.top().second;
		q.pop();
		if(v[x])
		continue;
		v[x]=1;
		for(int i=head[x];i;i=Next[i]){
			int y=ver[i],z=edge[i];
			if(d[y]>d[x]+z){
				d[y]=d[x]+z;
				q.push(make_pair(-d[y],y));
			} 
		}
	}
}
inline void dijstra(int s){
	for(int i=1;i<=n;i++)
	D[i]=inf,v[i]=0;
	D[s]=0;
	q.push(make_pair(0,s));
	while(!q.empty())
	{
		int x=q.top().second;
		q.pop();
		if(v[x])
		continue;
		v[x]=1;
		for(int i=head2[x];i;i=Next2[i]){
			int y=ver2[i],z=edge2[i];
			if(D[y]>D[x]+z){
				D[y]=D[x]+z;
				q.push(make_pair(-D[y],y));
			} 
		}
	}
}
struct node{
	int x,y,len;
}a[M];
int res=0;
void clear()
{
	tot=0;
	tot2=0; 
	mp.clear();
	memset(head,0,sizeof(head));
	memset(ver,0,sizeof(ver));
	memset(edge,0,sizeof(edge));
	memset(Next,0,sizeof(Next));
	memset(head2,0,sizeof(head2));
	memset(ver2,0,sizeof(ver2));
	memset(edge2,0,sizeof(edge2));
	memset(Next2,0,sizeof(Next2));
}
inline int change(char *s)
{
    if(mp.count(s)) return mp[s];
    else return mp[s]=++cnt;
}
signed main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	while(~scanf("%lld%lld",&n,&m)){
		clear();
		cnt=0;
		for(int i=1;i<=m;i++)
		{
			scanf("%s %s %lld",s1,s2,&l);
//			printf("%s--%s\n",s1,s2); 
			a[i].x=change(s1);
			a[i].y=change(s2);
			a[i].len=l;
			add(a[i].x,a[i].y,a[i].len);
			add2(a[i].y,a[i].x,a[i].len);
		}
		n=max(n,cnt);
		scanf("%s %s",s1,s2);
//		printf("%s--%s\n",s1,s2); 
		int st=change(s1),ed=change(s2);
//		for(map<string,int>::iterator it=mp.begin();it!=mp.end();it++)
//		{
//			cout<<it->first<<"  "<<it->second<<endl;
//		}
		
		if(st==0||ed==0)
		{
			
			puts("-1");
//			cout<<st<<"---"<<ed<<endl;
			continue;
		} 
		
		
		Dijstra(st);
		dijstra(ed);
//		for(int i=1;i<=n;i++) 
//		cout<<i<<" ````"<<d[i]<<endl;
		
		
		
//		for(int i=1;i<=n;i++) 
//		cout<<i<<"  ===  "<<D[i]<<endl;
		int ans=inf;
		for(int i=1;i<=m;i++)
		{
			int x=a[i].x;
			int y=a[i].y;
			int len=a[i].len;
			len/=2;
			ans=min(ans,d[x]+len+D[y]);
		}
		if(ans>=inf)
		ans=-1;
		printf("%lld\n",ans);
	}
    return 0;
}

I:prufer序列
题意:
给定n个点,要求1-k个点可以分析:
为了满足情况1,需要形成一颗以1号节点为根节点的树,然后,剩下的k-1个点可以任意连接。所以对于第二部分,他的结果就是(n-k)^(n-k),然后对于第一部分,请看下面
在这里插入图片描述∴ 题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Inf 0x3f3f3f3f
#define PI  acos(-1.0)
using namespace std;
typedef long long ll;
const double eps = 1e-6;
const int N = 1005;
const int mod=1e9+7;
int n,k;
ll ppow(ll a,ll b)
{
	ll res=1;
	while(b)
	{
		if(b&1)
		{
			res*=a;
			res%=mod;
		}
		a*=a;
		a%=mod;
		b>>=1;
	}
	return res;
}
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>k;
//	init();
	ll ans=pow(k,k-2);ans%=mod;
//	ll ans=0;
//	for(int i=1;i<=k-1;i++)
//	{
		cout<<"::"<<comb[k-1][i]<<"=="<<a[i]<<"===="<<ppow(i+1,k-i-1)<<endl;
//		ans+=comb[k-1][i]*a[i]*ppow(i+1,k-1-i)%mod;
		cout<<ans<<"  "<<i<<endl;
//	}
//	cout<<"====="<<ans<<endl;
	ans*=ppow(n-k,n-k);
	ans*=k;ans%=mod;
	ans%=mod;
	cout<<ans<<endl; 
    return 0;
}

J:线段树
题意:
给定一个序列,a[1-n],我要依次删除下标为b[1-n]的数字,每次询问连续块的权值和最大为多少
分析:
可以利用求最大子段和的线段树做,一开始把所有节点初始化为 -∑ai ,然后从从后往前加点b[n-1]。每次再查询最大子段和,这样就可以了。
题解:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<map>
#include<set>
#include<stack>
#define int ll

using namespace std;
typedef long long ll;


const int N=1e6+10;
int n,m;
int op;
int num[N];
struct node{
    int l,r;
    ll lmax,rmax,maxx,sum;
}a[N<<2];
int x,y;

int aa[N];
int b[N];
ll sum=0;
void pushup(int k)
{
    a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
    a[k].lmax=max(a[k<<1].lmax,a[k<<1].sum+a[k<<1|1].lmax);
    a[k].rmax=max(a[k<<1|1].rmax,a[k<<1|1].sum+a[k<<1].rmax);
    a[k].maxx=max(max(a[k<<1].maxx,a[k<<1|1].maxx),a[k<<1].rmax+a[k<<1|1].lmax);
}
void build(int k,int l,int r)
{
    a[k].l=l;
    a[k].r=r;
    if(l==r)
    {
        a[k].sum=a[k].lmax=a[k].rmax=a[k].maxx=sum;
        return ;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    pushup(k);
}
void change(int k,int x,int y)//单点更新
{
    if(a[k].l==a[k].r)
    {
        a[k].sum=a[k].lmax=a[k].rmax=a[k].maxx=y;
        return ;
    }
    int mid=(a[k].l+a[k].r)>>1;
    if(x<=mid)
    change(k<<1,x,y);
    else
    change(k<<1|1,x,y);
    pushup(k);
}
node query(int k,int l,int r)
{
    if(a[k].l>=l&&a[k].r<=r)
    {
        return a[k];
    }
    int mid=(a[k].l+a[k].r)>>1;
    if(r<=mid)
    return query(k<<1,l,r);
    else if(l>mid)
    return query(k<<1|1,l,r);
    else
    {
        node ll,rr,ret;
        ll=query(k<<1,l,mid);
        rr=query(k<<1|1,mid+1,r);
        ret.sum=ll.sum+rr.sum;
        ret.lmax=max(ll.lmax,ll.sum+rr.lmax);
        ret.rmax=max(rr.rmax,rr.sum+ll.rmax);
        ret.maxx=max(max(ll.maxx,rr.maxx),ll.rmax+rr.lmax);
        return ret;
    }
}
signed main()
{
    cin>>n;
    
    for(int i=1;i<=n;i++)
    cin>>aa[i],sum+=aa[i];
    for(int i=1;i<=n;i++)
    cin>>b[i];
    sum*=-1;
    build(1,1,n);
    ll ans[N]={0};
    for(int i=n;i>=1;i--)
    {
        change(1,b[i],aa[b[i]]);
        ans[i]=a[1].maxx;
    }
    for(int i=2;i<=n;i++)
    {
    	cout<<ans[i]<<endl;
	}
	cout<<0<<endl;
    return 0;
}

K:dp
题意:
每个位置有个权值a[i],我一开始站在1处,我可以往右或者往左,不能超出1-n。问我走k步,其中往左走不超过z步我能得到的权值和最大值。
思路:
利用dp的想法,dp[i][j]代表,我向右走i步,向左走j步能得到的最大值,当往右i步,往左j步,我处于的位置肯定是a[i-j+1],只能由i-1,j 向右走或者是 i j-1 向左走。那么转移方程为:
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i-j+1];
注意,因为不能超出1-n的范围,所以j一定小于等于i。
题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Inf 0x3f3f3f3f
#define PI  acos(-1.0)
using namespace std;
typedef long long ll;
const double eps = 1e-6;
const int N = 1e5+5;
const int mod=1e9+7;
int n,k,z;
int a[N];
ll dp[N][10];//you i,zuo j
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;
	cin>>t;
	while(t--)
	{
		cin>>n>>k>>z;
		for(int i=1;i<=n;i++)
			cin>>a[i];
		for(int i=0;i<=k;i++)
		{
			for(int j=0;j<=z;j++)
			dp[i][j]=0;
		}
//memset(dp,0,sizeof(dp));
		dp[0][0]=a[1];
		for(int i=1;i<=k;i++)//you
		{
			dp[i][0]=dp[i-1][0]+a[i+1];
			for(int j=1;j<=z&&j<=i;j++)//zuo
			{
				dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i-j+1];
			}
		} 
		ll ans=0;
		for(int i=0;i<=z;i++)
		{
			ans=max(ans,dp[k-i][i]);
		}
		cout<<ans<<endl;
	}
    return 0;
}

L:防AK

M:KMP的next数组理解
题意:
对于给定字符串s,求满足,既是前缀,又是后缀,又出现在字符串中间的最大子串。
分析:
因为要满足既是前缀,又是后缀,就可以想到,next数组的应用。next[i]代表的含义为0–next[i]-1,与i-next[i]+1 --i是相互匹配的。所以前后缀相匹配的最大长度为next[len]
问题就是如何判断是否在中间出现过。很容易想到,直接看一下next[len]是否在中间出现过就可以,一提交,是错的,又想到对于aaaaaaaa这种,是没法处理的。搜索题解,看到一种写法,先上代码,后面分析为什么这样干。
题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Inf 0x3f3f3f3f
#define PI  acos(-1.0)
using namespace std;
typedef long long ll;
const double eps = 1e-6;
const int N = 1e6+5;
const int mod=1e9+7;
char s[N];
int nxt[N];
int n;
map<int,bool> vis;
void getnext()
{
	int i,j;
	j=nxt[0]=-1;
	i=0;
	while(i<n)
	{
		while(j!=-1&&s[i]!=s[j])
		j=nxt[j];
		nxt[++i]=++j;
	}
}
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	scanf("%s",s);
	n=strlen(s);
	if(n<=2)
	{
		puts("Just a legend");
		return 0;
	}
	getnext();
	int ans=nxt[n];
	if(ans<=0)
	{
		puts("Just a legend");
		return 0;
	}
	for(int i=0;i<n;i++)
	vis[nxt[i]]=true;
	while(!vis[ans]&&ans)
	ans=nxt[ans];
	if(ans<=0)
	{
		puts("Just a legend");
		return 0;
	}
	for(int i=0;i<ans;i++)
		printf("%c",s[i]);
	printf("\n");
    return 0;
}

可以看到,他中间有个这样的操作:

while(!vis[ans]&&ans)
	ans=nxt[ans];

这是什么含义呢,ans进行更新的条件为ans>0&&ans没出现过。因为在计算vis的时候只计算了0-n-1的,当ans没出现过,就只能证明此事的ans不符合答案,因为必须中间也需要出现。那么就在找这个0-next[ans]-1内前后匹配的字符串,依次进行,所以此时得到的才是答案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值