noip模拟测试37

考试总结:这次考试,难度不是很大,但是成绩却不太理想。看完题目,我多多少少都有一些思路,T1我看出了 \(ax+by=c\) 的不定方程求解,但我把板子给忘了,搞了快两个小时才搞出一组特解,但是因为特解会有负数,我当时就怀疑思路的正确性,也没仔细想,因为时间比较紧张,就把它弃了,然后是T2,我觉得和就是一道简单dp 加上线段树优化,码完了发现不对,这时候还剩差不多一个小时,我把自己hack之后也没有更正思路,就打了个测试点分治,然后搞T3,我觉得这是个最小生成树,边想边打,打完了发现自己思路不对,应该跑最短路,就打了个暴力的最短路,然后T4,觉得是个并查集,当时觉得根据真假在每个人之间连边,但是没有实现出来,总体来说,这次考试时间把握不够合理,发挥不是很好,同时我的一些知识点掌握不扎实,应该多加复习。

T1 数列

思路:对于一个数,我们要解方程 ax+by=m,并最小化 abs(x)+abs(y)。先用 exgcd
求出一组解(x’,y’),那么解集是(x’+kb,y’-ka),
证明: 若 ax+by=m,则 ax+by+kab-kab=m,移项,合并即可。
显然 x 只会取最小的正值或最大的负值,那么我们就分别计算,取最小即可。
补充芝士:扩展欧几里得,求 \(ax+by=gcd(a,b)\),
\(gcd(a,b)=gcd(b,a\% b)\)
\(ax_1+by_1=gcd(a,b)\)
\(bx_2+(a\%b)y2=gcd(b,a\% b)\)
我们可以将(a%b)写成\((a-\lfloor\frac{a}{b}\rfloor*b)\)
将括号打开常数a,b合并,合并之后的结果为\(a*y_2+b*(x_2-\lfloor\frac{a}{b}\rfloor*y_2)\)
我们将两式子联立,对比系数即可得到\(x_1=y_2\),\(x_1=y_2,y_1=x_2-\lfloor\frac{a}{b}\rfloor*y_2\)
这样就可以求得一组x,y,那么一组满足条件(\(ax+by=c\))的特解 x 即为 \((x*(c/gcd)\%(b/gcd)+(b/gcd))\%(b/gcd)\)
代码如下:

AC_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=1e5+10;
int n,a,b,c,x,y;
int ans,gcd;
ii read()
{
	int x=0;
	bool f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
iv oj(int i,int j)
{
	if(j==0)
	{
		x=1;
		y=0;
		gcd=i;
		return;
	}
	oj(j,i%j);
	int tmp=x;
	x=y;
	y=tmp-i/j*y;
}
signed main()
{
	n=read();
	a=read();
	b=read();
	if(a<b)
		swap(a,b);
	oj(a,b);
	a/=gcd;
	b/=gcd;
	int x1,y1,x2,y2,out;
	bool br=0;
	for(re i=1;i<=n;i++)
	{
		c=read();
		if(c%gcd!=0||br)
		{
			br=1;
			continue;
		}
		c/=gcd;
		x1=(x*c+b)%b;
		y1=(c-a*x1)/b;
		out=abs(x1)+abs(y1);
		if(x1<0)
		{
			x2=x1+((-x1)/b+1)*b;
			y2=y1-((-x1)/b+1)*a;
			out=min(out,abs(x2)+abs(y2));
			
			x2=x1+((-x1)/b)*b;
			y2=y1-((-x1)/b)*a;
			out=min(out,abs(x2)+abs(y2));
		}
		else
		{
			x2=x1-(x1/b)*b;
			y2=y1+(x1/b)*a;
			out=min(out,abs(x2)+abs(y2));
			
			x2=x1-(x1/b+1)*b;
			y2=y1+(x1/b+1)*a;
			out=min(out,abs(x2)+abs(y2));
		}
		if(y1<0)
		{
			y2=y1+((-y1)/a+1)*a;
			x2=x1-((-y1)/a+1)*b;
			out=min(out,abs(x2)+abs(y2));
			
			y2=y1+((-y1)/a)*a;
			x2=x1-((-y1)/a)*b;
			out=min(out,abs(x2)+abs(y2));
		}
		else
		{
			y2=y1-(y1/a)*a;
			x2=x1+(y1/a)*b;
			out=min(out,abs(x2)+abs(y2));
			
			y2=y1-(y1/a+1)*a;
			x2=x1+(y1/a+1)*b;
			out=min(out,abs(x2)+abs(y2));
		}
		ans+=out;
	}
	if(br)
		printf("-1\n");
	else
		printf("%lld\n",ans);
	return 0;
}

T2 数对

这道题与之前的一道考试题比较类似,就是多了一个排序,如果不考虑排序问题,那么对于一个给定的序列S,我们就是要求出满足条件的最大权值和,设\(f_{i,j}\)表示前 i 个数中,最大a值为 j 的最大权值之和,那么考虑转移,因为限制条件同时有a,b, 所以我们根据当前 a与 b 的大小关系来分情况讨论,

  1. \(a_i<b_i\) 那么\(f_{i,j}=max(f_[i,j],f_{i-1,k}+val[i]) (1<=k<=a_i)\)
    \(f_{i,j}=max(f_{i,j},f_{i-1,j}+val_i) ,(a_i+1<=j<=b_i)\)
    2.\(a_i>=b_i\) 那么 \(f_{i,j}=max(f_{i,j},f_{i-1,k}+val_i),(1<=k<=b_i)\)
    那么现在我们考虑顺序问题,考虑两个数对(a i ,b i )和(a j ,b j ),如果 a i <b j 并且 b i >a j ,那么我们望 i 排在j 前面。对于相反的情况,我们希望 j 排在 i 前面。其余两种情况显然按 a+b 从小到大排列就可以满足所有的需求,
60pts_朴素DP


#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=1e5+10;
int n,cnt,maxx,sum;
int f[3100][6100],lsh[N<<1];
struct node
{
	int x,y,w;
	friend bool operator < (node a,node b)
	{
		if(a.x<b.y and b.x>a.y)
			return 1;
		else if(a.x>b.y and b.x<a.y)
			return 0;
		return (a.x+a.y)<(b.x+b.y); 
	}
}use[N];
ii read()
{
	int x=0;
	bool f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
signed main()
{
	n=read();
	int a,b,c;
	for(re i=1;i<=n;i++)
	{
		a=read();
		b=read();
		c=read();
		use[i].x=a;
		use[i].y=b;
		use[i].w=c;
		lsh[++cnt]=a;
		lsh[++cnt]=b;
	}
	sort(use+1,use+n+1);
	sort(lsh+1,lsh+cnt+1);
	cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
	for(re i=1;i<=n;i++)
	{
		a=use[i].x;
		b=use[i].y;
		a=lower_bound(lsh+1,lsh+cnt+1,a)-lsh;
		b=lower_bound(lsh+1,lsh+cnt+1,b)-lsh;
		use[i].x=a;
		use[i].y=b;
		sum+=use[i].w;
	}
	f[1][use[1].x]=use[1].w;	
	for(re i=2;i<=n;i++)
	{
		for(re j=1;j<=cnt;j++)
			f[i][j]=f[i-1][j];
		if(use[i].x<=use[i].y)
		{
			for(re j=1;j<=use[i].x;j++)
				f[i][use[i].x]=max(f[i][use[i].x],f[i-1][j]+use[i].w);
			for(re j=use[i].x+1;j<=use[i].y;j++)
				f[i][j]=max(f[i][j],f[i-1][j]+use[i].w);
		}
		else
		{
			for(re j=1;j<=use[i].y;j++)
				f[i][use[i].x]=max(f[i][use[i].x],f[i-1][j]+use[i].w);
		}
	}
	for(re i=1;i<=n;i++)
	{
		for(re j=1;j<=cnt;j++)
			maxx=max(maxx,f[i][j]);
	}
	cout<<maxx<<endl;
	return 0;
}

AC_code,线段树优化
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=2e5+10;
int n,cnt,maxx,sum,ans;
int lsh[N<<1];
struct node
{
	int x,y,w;
	friend bool operator < (node a,node b)
	{
		if(a.x<b.y and b.x>a.y)
			return 1;
		else if(a.x>b.y and b.x<a.y)
			return 0;
		return (a.x+a.y)<(b.x+b.y); 
	}
}use[N];
ii read()
{
	int x=0;
	bool f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
struct Segment_Tree
{
	int dp[N<<2],lazy[N<<2];
	iv pp(int rt)
	{
		dp[rt]=max(dp[lc],dp[rc]);
	}
	iv pd(int rt)
	{
		if(lazy[rt])
		{
			dp[lc]+=lazy[rt];
			dp[rc]+=lazy[rt];
			lazy[lc]+=lazy[rt];
			lazy[rc]+=lazy[rt];
			lazy[rt]=0;
		}
	}
	iv insert(int rt,int l,int r,int p,int z)
	{
		if(l==r)
		{
			dp[rt]=max(dp[rt],z);
			return;
		}
		pd(rt);
		if(mid>=p)
			insert(lc,l,mid,p,z);
		else
			insert(rc,mid+1,r,p,z);
		pp(rt);
	}
	iv updata(int rt,int l,int r,int L,int R,int z)
	{
		if(L>R||L>r||R<l)
			return;
		if(L<=l&&r<=R)
		{
			dp[rt]+=z;
			lazy[rt]+=z;
			return;
		}
		pd(rt);
		if(mid>=L)
			updata(lc,l,mid,L,R,z);
		if(mid<R)
			updata(rc,mid+1,r,L,R,z);
		pp(rt);
	}
	ii query(int rt,int l,int r,int L,int R)
	{
		if(L>R)
			return 0;
		if(L<=l&&r<=R)
			return dp[rt];
		pd(rt);
		if(mid>=R)
			return query(lc,l,mid,L,R);
		if(mid<L)
			return query(rc,mid+1,r,L,R);
		return max(query(lc,l,mid,L,R),query(rc,mid+1,r,L,R));
	}
}T;
signed main()
{
	n=read();
	int a,b,c;
	for(re i=1;i<=n;i++)
	{
		a=read();
		b=read();
		c=read();
		use[i].x=a;
		use[i].y=b;
		use[i].w=c;
		lsh[++cnt]=a;
		lsh[++cnt]=b;
	}
	sort(use+1,use+n+1);
	sort(lsh+1,lsh+cnt+1);
	cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
	for(re i=1;i<=n;i++)
	{
		a=use[i].x;
		b=use[i].y;
		a=lower_bound(lsh+1,lsh+cnt+1,a)-lsh;
		b=lower_bound(lsh+1,lsh+cnt+1,b)-lsh;
		use[i].x=a;
		use[i].y=b;
	}
	for(re i=1;i<=n;i++)
	{
		ans=0;
		if(use[i].x<use[i].y)
		{
			ans=T.query(1,1,cnt,1,use[i].x);
			T.insert(1,1,cnt,use[i].x,ans+use[i].w);
			T.updata(1,1,cnt,use[i].x+1,use[i].y,use[i].w);
		}
		else
		{
			ans=T.query(1,1,cnt,1,use[i].y);
			T.insert(1,1,cnt,use[i].x,ans+use[i].w);	
		}
	}
	printf("%lld\n",T.query(1,1,cnt,1,cnt));
	return 0;
}

T3 最小距离

思路:这道题说白了就是一个多源最短路,在我们跑单源最短路(Dij)的时候我们刚开始将一个起点压进堆,这道题我们将所有特殊点压进堆里,然后更新出所有的dis数组,结束后我们枚举每条边,如果这条边两端的点不是由同一个源点更新的,那么就更新这两个源点之间的答案。
代码如下:

AC_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
#define next nettrr
#define head hheees
using namespace std;
const int N=2e5+10;
const int INF=1e17;
struct node
{
	int s,w;
	friend bool operator < (node a,node b)
	{
		return a.w>b.w;
	}
};
struct CUN
{
	int st,ed,w;
}use[N];
priority_queue<node> Q;
int n,m,p,tot;
int to[N<<1],next[N<<1],head[N],val[N<<1];
int csp[N],dis[N],be[N],my_min[N];
bool sp[N],vis[N];
ii read()
{
	int x=0;
	bool f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
iv add(int x,int y,int z)
{
	to[++tot]=y;
	next[tot]=head[x];
	head[x]=tot;
	val[tot]=z;
}
ii gett(int x)
{
	if(x==be[x])
		return x;
	return be[x]=gett(be[x]);
}
iv dj()
{
	for(re i=1;i<=n;i++)
	{
		dis[i]=INF;
		my_min[i]=INF;	
	}
	for(re i=1;i<=n;i++)
		be[i]=i;
	for(re i=1;i<=p;i++)
	{
		dis[csp[i]]=0;
		Q.push((node){csp[i],0});	
	}
	while(!Q.empty())
	{
		int x=Q.top().s;
		int y=Q.top().w;
		Q.pop();
		if(!vis[x])
		{
			vis[x]=1;
			for(re i=head[x];i;i=next[i])
			{
				int p=to[i];
				if(dis[p]>dis[x]+val[i])
				{
					dis[p]=dis[x]+val[i];
					be[p]=gett(x);
					Q.push((node){p,dis[p]});
				}
			}
		}
	}
	for(re i=1;i<=m;i++)
	{
		int fx=gett(use[i].st),fy=gett(use[i].ed);
		if(fx!=fy)
		{
			my_min[fx]=min(my_min[fx],dis[use[i].st]+dis[use[i].ed]+use[i].w);
			my_min[fy]=min(my_min[fy],dis[use[i].st]+dis[use[i].ed]+use[i].w);
		}
	}
	for(re i=1;i<=p;i++)
		printf("%lld ",my_min[csp[i]]);
}
signed main()
{
	n=read();
	m=read();
	p=read();
	for(re i=1;i<=p;i++)
	{
		csp[i]=read();
		sp[csp[i]]=1;
	}
	int u,v,w;
	for(re i=1;i<=m;i++)
	{
		u=read();
		v=read();
		w=read();
		use[i].st=u;
		use[i].ed=v;
		use[i].w=w;
		add(u,v,w);
		add(v,u,w);
	}
	dj();
	return 0;
}

T4 真相

image

65pts_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=1010;
int n,t;
char s[5];
int js[N],cun[N],h[N];
int pd[N];
ii read()
{
	int x=0;
	bool f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
inline bool check()
{
	for(re i=2;i<=n;i++)
	{
		if(pd[i-1]==1)
		{
			if(cun[i-1]==-1)
				pd[i]=1;
			else
				pd[i]=2;
		}
		else if(pd[i-1]==2)
		{
			if(cun[i-1]==-1)
				pd[i]=2;
			else
				pd[i]=1;
		}
	}
	if(pd[n]==1)
	{
		if(cun[n]==-1 and pd[1]==1)
			return 1;
		else if(cun[n]==-2 and pd[1]==2)
			return 1;
		return 0;
	}
	else if(pd[n]==2)
	{
		if(cun[n]==-1 and pd[1]==1)
			return 0;
		else if(cun[n]==-2 and pd[1]==2)
			return 0;
		return 1;
	}
}
iv spe_1()
{
	int cnt=0;
	memset(pd,0,sizeof(pd));
	pd[1]=1;
	if(check())
	{
		printf("consistent\n");
		return;
	}
	memset(pd,0,sizeof(pd));
	pd[1]=2;
	if(check())
		printf("consistent\n");
	else
		printf("inconsistent\n");
	return ;
}
inline bool check_2(int x)
{
	int cnt=0;
	for(re i=1;i<=h[0];i++)
	{
		int now=h[i];
		while(1)
		{
			int la=now-1;
			if(la==0)
				la=n;
			if(pd[la])
				break;
			if(pd[now]==1)
			{
				if(cun[la]==-1)
					pd[la]=1;
				else
					pd[la]=2;
			}
			else
			{
				if(cun[la]==-1)
					pd[la]=2;
				else
					pd[la]=1;
			}
			now=la;
		}
	}
	for(re i=1;i<=n;i++)
		if(pd[i]==1)
			++cnt;
	if(cnt==x)
		return 1;
	return 0;
}
iv spe_2()
{
	bool ke=0;
	for(re i=0;i<=n;i++)
	{
		memset(pd,0,sizeof(pd));
		for(re j=1;j<=h[0];j++)
		{
			if(cun[h[j]]!=i)
				pd[h[j]]=2;
			else
				pd[h[j]]=1;
		}
		if(check_2(i))
		{
			ke=1;
			break;
		}
	}
	if(ke)
		printf("consistent\n");
	else
		printf("inconsistent\n");
	return ;
}
signed main()
{
	t=read();
	while(t--)
	{
		n=read();
		bool hav=0;
		memset(cun,0,sizeof(cun));
		memset(h,0,sizeof(h));
		for(re i=1;i<=n;i++)
		{
			scanf("%s",s);
			if(s[0]=='$')
			{
				hav=1;
				cun[i]=read();
				h[++h[0]]=i;
			}
			else if(s[0]=='+')
			{
				cun[i]=-1;	
			}
			else if(s[0]=='-')
			{
				cun[i]=-2;
			}	
		}
		if(!hav)
			spe_1();
		else
			spe_2();
	}
	return 0;
}

posted @ 2021-08-13 19:41  WindZR  阅读(41)  评论(0编辑  收藏  举报