2021-06-12组队模拟

组队模拟

目录

概况

本次模拟采用组队形式,题目难度橙-蓝-蓝

A.cubicp

思路

首先觉得有点难,后来仔细看看,dlm又推了推式子,再读个题,半小时顺利切掉,具体推导如下:

一个 P 数如果是立方差数,不妨假设是 x3-y3(x>y)。相当于(x-y)(x2+xy+y2),由于 P 是质数,因此 x=y+1。又有(x-1)2+x2+x(x-1)=P,暴力枚举 1~10^6 判断即可。

代码

#include <bits/stdc++.h>
using namespace std;
long long n;
bool flag=1;
long long js(long long x){
    
    
	return 3*x*x+3*x+1;
}
int main(){
    
    
	freopen("cubicp.in","r",stdin);
	freopen("cubicp.out","w",stdout);
	long long q,p;
	scanf("%lld",&q);
	for(int i=1;i<=q;i++){
    
    
		flag=1;
		scanf("%lld",&p);
		for(int j=1;j<=600000;j++){
    
    
			if(js(j)==p){
    
    
				printf("YES\n");
				flag=0;
				break;
			}
		}	
		if(flag==1)printf("NO\n");
	}
	return 0;
}

歧途

开始的时候没有抓住质数这个点,只是去想公式怎么推,但后来发现质数才是解题关键,要小心思维陷阱

B.dp

思路

本题第一眼以为是线段树,但是显然没有具体维护的值,所以推测应该是区间dp,dp[i][j]表示前i个数分为j段时的价值,枚举这个断点k,有dp[i][j]=min{dp[k][j-1]+sum(k+1,i)},但是这个算法显然时间是假的,观察一下式子符合决策单调性的条件,所以需要决策单调性优化

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100010;
int c[N],a[N];
ll f[N],g[N];
int p,q,n,k;
ll tot;
void move(int l,int r)
{
    
    
	while (l<p) p--,tot+=c[a[p]],c[a[p]]++;
	while (r>q) q++,tot+=c[a[q]],c[a[q]]++;
	while (p<l) c[a[p]]--,tot-=c[a[p]],p++;
	while (r<q) c[a[q]]--,tot-=c[a[q]],q--;
}
void work(int l,int r,int fl,int fr)
{
    
    
	if (fl>fr) return;
	int mid=(fl+fr)>>1,mi;
	ll mx=1LL<<60;
	for (int i=l;i<=r;i++)
	if (i<mid)
	{
    
    
		move(i+1,mid);
		if (f[i]+tot<mx) mx=f[i]+tot,mi=i;
	}
	g[mid]=mx;
	work(l,mi,fl,mid-1);
	work(mi,r,mid+1,fr);
}
int main()
{
    
    
    //freopen("dp.in","r",stdin);
    //freopen("dp.out","w",stdout);
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	f[0]=0;
	for (int i=1;i<=n;i++) f[i]=1LL<<60;
	while (k--)
	{
    
    
		p=1,q=0,tot=0;
		for (int i=1;i<=n;i++) c[i]=0;
		work(0,n-1,1,n);
		for (int i=0;i<=n;i++) f[i]=g[i],g[i]=0;
	}
	printf("%lld",f[n]);
	return 0;
}

历程

在写暴力dp的路上没什么波澜但是一去不复返,60分还比较好拿,但是在优化时发现自己决策单调性优化学了个寂寞,于是遗憾卡40,最后还得跟学长乖乖学。但是没太学会,还得再看(
注:move部分因为借鉴了一点莫队的思路,所以时间复杂度容易算错,不会出现根号,而是纯的O(nlogn)

C.number

思路

对于每两个区间,求区间交与区间并,显然,若两个区间值相同,则交不能为空,若两个区间值不同,则当值较大时,不能在添加一个值较小的区间,即排序后查询区间交,合法情况下使区间并删除,采用线段树或并查集维护

代码

#include<bits/stdc++.h>
#define mod 1000000007
#define ll long long
#define N 1000100
#define mid (l+r>>1)
using namespace std;
inline int read(){
    
    
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){
    
    if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){
    
    x=x*10+c-'0';c=getchar();}
    return x*f;
}
struct yjx{
    
    int l,r,x;}a[N],t[N];
int fa[2*N],n,T;
bool cmp(yjx x,yjx y){
    
    return x.x>y.x;}
int find(int x){
    
    return fa[x]==x?x:fa[x]=find(fa[x]);}
bool check(int x){
    
    
	for(int i=1;i<=n*2;i++)fa[i]=i;
	for(int i=1;i<=x;i++)t[i]=a[i];
	sort(t+1,t+x+1,cmp);
	int lmin,lmax,rmin,rmax;
	lmin=lmax=t[1].l;
	rmin=rmax=t[1].r;
	for(int i=1;i<=x;i++){
    
    
		if(t[i].x<t[i-1].x){
    
    
			if(find(lmax)>rmin)return 1;
			for(int j=find(lmin);j<=rmax;j++)fa[find(j)]=find(rmax+1);
			lmin=lmax=t[i].l;
			rmin=rmax=t[i].r;
		} else{
    
    
			lmin=min(lmin,t[i].l);
			lmax=max(lmax,t[i].l);
			rmin=min(rmin,t[i].r);
			rmax=max(rmax,t[i].r);
			if(lmax>rmin)return 1;
		}
	}
	if(find(lmax)>rmin)return 1;
	return 0;
}
int main(){
    
    
	n=read(),T=read();int l=1,r=n;
	for(int i=1;i<=T;i++)a[i].l=read(),a[i].r=read(),a[i].x=read();
	while(l<r){
    
    if(check(mid))r=mid;else l=mid+1;}
	printf("%d",l);
    return 0;

从塞罕坝到ybt黑洞

在做题的途中,隔壁巨神提出了一种塞罕坝做法:种一千棵线段树!我仔细思索了一下, 发现这种做法显然可以用枚举曾经出现过的操作特判解决,可以轻松得到八十分,时间复杂度O(n2),但是明知道正解会带log可是不知道怎么解决,于是……在考试结束后,学长公布了正确的答案,二分加并查集,没错ybt黑洞并查集,而且思路很清奇,使相邻的节点成为并查集

D.edit

思路

我们不需要考虑修改内容是什么,只需要考虑进行哪一步操作,显然如果一样,可以直接从上一位转移,若不一样,则可以插入、删除或修改,易得状态转移方程:

if(s1[i]==s2[j])dp[i][j]=dp[i-1][j-1];
else dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;

代码

#include<bits/stdc++.h>
#define N 2007
#define ll long long
using namespace std;
int dp[N][N],len1,len2,len;
char s1[N],s2[N];
int main(){
    
    
	char c=getchar();while(c!='\n'){
    
    s1[++len1]=c;c=getchar();}
	c=getchar();while(c!='\n'){
    
    s2[++len2]=c;c=getchar();}
	memset(dp,0x3f,sizeof(dp));dp[0][0]=0;
	for(int i=1;i<=len1;i++)dp[i][0]=i;
	for(int i=1;i<=len2;i++)dp[0][i]=i;
	for(int i=1;i<=len1;i++)
		for(int j=1;j<=len2;j++){
    
    
			if(s1[i]==s2[j])dp[i][j]=dp[i-1][j-1];
			else dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
		}
	printf("%d",dp[len1][len2]);
	return 0;
}

“我样例出错啦!!!”

最开始看这道题确实有一点恍惚,进入了作者设下的思维陷阱,开始考虑修改之后的内容,本来想通过最长公共子序列解决,但很快被我自己hack掉了(这是个伏笔),然后老师公布答案,才发现根本不需要考虑修改内容,于是快速写完了代码。但是!我自己造的数据和我手动模拟答案不一样!!!找各路大佬debug,最后冒险交了一下,过了?!!!!!,最后在周围人的帮助下,才发现,数据,出错了……样例不谨慎,程序两行泪呜呜呜

猜你喜欢

转载自blog.csdn.net/MuLaSaMe/article/details/117847199