812考試總結(NOIP模擬37)[數列·數對·最小距離·真相]

Varuxn發表於2021-08-12

前言

考得挺憋屈的。。。

先是搞了兩個半小時的 T1 後來發現假了,又沒多想跳了。。

然後一看 T2 這不是隊長快跑嘛。。。

先是根據自己的想法打了一遍(考完之後發現是對的。。)

然後回想了一下之前的題,不對呀,我記得有一個 if-else 的。

接下來我就這麼改了,然後連樣例都過不去了。。。

後來 40min 碼完了兩個暴力(還有一個打錯了。。)

最後剩下了 30min 又去看 T1 了,推出來了半個正解 50pts

感覺哪個都有一點感覺,會但不是完全會。。

T1 數列

解題思路

擴充套件歐幾里德。。。

發現當 x 為 1 的時候可以直接用擴充套件歐幾里德求出來。

也可以進而求出其他數的一組可行但不一定是最優的解。

假設已經有 \(ax'+by'=m\)

可以推出: \(ax'+kab+by'-kab=m\)

因此可以得出解集: \((x'+kb,y'-ka)\)

然後取最小的正值或者最大的負值就好了。

注意這裡最優的 x 不一定對應最優的 y 。

code

50pts

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e5+10;
int n,a,b,g,lcm,ans,s[N],w[N];
struct Node
{
	int x,y;
};
int gcd(int x,int y)
{
	if(!y)	return x;
	return gcd(y,x%y);
}
Node exgcd(int a,int b)
{
	if(!b)	return (Node){1,0};
	Node temp=exgcd(b,a%b);
	int x=temp.x,y=temp.y;
	return (Node){y,x-a/b*y};
}
signed main()
{
	n=read();	a=read();	b=read();
	g=gcd(a,b);
	Node t=exgcd(a,b);
	t.x=abs(t.x);	t.y=abs(t.y);
	for(int i=1;i<=n;i++)
	{
		s[i]=read();
		if(s[i]<0)	s[i]=-s[i];
		if(s[i]%g){printf("-1");return 0;}
	}
	for(int i=1;i<=n;i++)
		s[i]/=g;
	a/=g;	b/=g;	lcm=a*b/g;
	for(int i=1;i<=n;i++)
	{
		int tmp1,tmp2,x,y;
		x=(s[i]*t.x/b)*b;
		y=(s[i]*t.y/a)*a;
		tmp1=min(abs(b+x-s[i]*t.x),abs(s[i]*t.x-x));
		tmp2=min(abs(a+y-s[i]*t.y),abs(s[i]*t.y-y));
		ans+=tmp1+tmp2;
	}
	printf("%lld",ans);
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e5+10,INF=1e18;
int n,a,b,g,lcm,ans,s[N],w[N];
struct Node
{
	int x,y;
};
int gcd(int x,int y)
{
	if(!y)	return x;
	return gcd(y,x%y);
}
Node exgcd(int a,int b)
{
	if(!b)	return (Node){1,0};
	Node temp=exgcd(b,a%b);
	int x=temp.x,y=temp.y;
	return (Node){y,x-a/b*y};
}
signed main()
{
	n=read();	a=abs(read());	b=abs(read());
	if(a>b)	swap(a,b);
	g=gcd(a,b);
	Node t=exgcd(a,b);
	for(int i=1;i<=n;i++)
	{
		s[i]=read();
		if(s[i]<0)	s[i]=-s[i];
		if(s[i]%g){printf("-1");return 0;}
	}
	a/=g;	b/=g;
	for(int i=1;i<=n;i++)	s[i]/=g;
	for(int i=1;i<=n;i++)
	{
		int num=s[i]/b;	s[i]%=b;
		int x=s[i]*t.x;
		int y=s[i]*t.y+num;
		int temp=abs(x)+abs(y);
		if(x>0)
		{
			int tmp=abs(x)/b;
			x-=tmp*b;	y+=tmp*a;
			temp=min(temp,abs(x)+abs(y));
			temp=min(temp,abs(x-b)+abs(y+a));
		}
		else
		{
			int tmp=abs(x)/b;
			x+=tmp*b;	y-=tmp*a;
			temp=min(temp,abs(x)+abs(y));
			temp=min(temp,abs(x+b)+abs(y-a));
		}
		ans+=temp;
	}
	printf("%lld",ans);
	return 0;
}

T2 數對

解題思路

首先證明一下按照 \(a+b\) 從大到小排序的正確性。

假設 i 位於 j 之前,那麼一定是 \(a_i<b_j\) 並且 \(b_i>a_j\) 更優。

所以此時的 \(a_i+b_i<a_j+b_j\)(嚴格來講應該有等於的情況)

然後對於相反的情況也是差不多,剩下兩種情況的排序的順序無所謂。。

接下來就是對於 \([1,min(a_i,b_i)]\) 以及 \([a_i+1,b_i]\) 這兩個區間的更新了。

設 DP陣列 \(f_{i,j}\) 表示排序之後的序列選前 i 個數,最大的 a 為 j 的最大值。。

發現這個可以線段樹優化,單點修改,區間修改,區間查詢就好了。

  • 注意:排序要在離散化之前,單點修改要取 max。

code

60pts

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e5+10,M=3e3+10;
int n,ans,f[M][M<<1];
int cnt,lsh[N];
struct Node
{
	int a,b,w;
}s[N];
bool comp(Node x,Node y)
{
	return x.a+x.b<y.a+y.b;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		s[i].a=read();	s[i].b=read();	s[i].w=read();
		lsh[++cnt]=s[i].a;	lsh[++cnt]=s[i].b;
	}
	sort(s+1,s+n+1,comp);
	sort(lsh+1,lsh+cnt+1);
	cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
	for(int i=1;i<=n;i++)
	{
		s[i].a=lower_bound(lsh+1,lsh+cnt+1,s[i].a)-lsh;
		s[i].b=lower_bound(lsh+1,lsh+cnt+1,s[i].b)-lsh;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=cnt;j++)
			f[i][j]=f[i-1][j];
		for(int j=1;j<=min(s[i].a,s[i].b);j++)
			f[i][s[i].a]=max(f[i][s[i].a],f[i-1][j]+s[i].w);
		for(int j=s[i].a+1;j<=s[i].b;j++)
			f[i][j]=max(f[i][j],f[i-1][j]+s[i].w);
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=cnt;j++)		
			ans=max(ans,f[i][j]);
	printf("%lld",ans);
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=2e5+10;
int n,ans,tre[N<<2],laz[N<<2];
int cnt,lsh[N];
struct Node
{
	int a,b,w;
}s[N];
bool comp(Node x,Node y)
{
	return x.a+x.b<y.a+y.b;
}
void push_up(int x)
{
	tre[x]=max(tre[ls],tre[rs]);
}
void push_down(int x)
{
	if(!laz[x])	return ;
	tre[ls]+=laz[x];
	tre[rs]+=laz[x];
	laz[ls]+=laz[x];
	laz[rs]+=laz[x];
	laz[x]=0;
}
int query(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)	return tre[x];
	push_down(x);
	int mid=(l+r)>>1,ans1=0,ans2=0;
	if(L<=mid)	ans1=query(ls,l,mid,L,R);
	if(R>mid)	ans2=query(rs,mid+1,r,L,R);
	push_up(x);
	return max(ans1,ans2);
}
void update(int x,int l,int r,int L,int R,int num)
{
	if(L<=l&&r<=R)
	{
		tre[x]+=num;
		laz[x]+=num;
		return ;
	}
	push_down(x);
	int mid=(l+r)>>1;
	if(L<=mid)	update(ls,l,mid,L,R,num);
	if(R>mid)	update(rs,mid+1,r,L,R,num);
	push_up(x);
}
void insert(int x,int l,int r,int pos,int num)
{
	if(l==r)
	{
		tre[x]=max(tre[x],num);
		return ;
	}
	push_down(x);
	int mid=(l+r)>>1;
	if(pos<=mid)	insert(ls,l,mid,pos,num);
	else	insert(rs,mid+1,r,pos,num);
	push_up(x);
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		s[i].a=read();	s[i].b=read();	s[i].w=read();
		lsh[++cnt]=s[i].a;	lsh[++cnt]=s[i].b;
	}
	sort(s+1,s+n+1,comp);
	sort(lsh+1,lsh+cnt+1);
	cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
	for(int i=1;i<=n;i++)
	{
		s[i].a=lower_bound(lsh+1,lsh+cnt+1,s[i].a)-lsh;
		s[i].b=lower_bound(lsh+1,lsh+cnt+1,s[i].b)-lsh;
	}
	for(int i=1;i<=n;i++)
	{
		if(s[i].b>s[i].a)	update(1,1,cnt,s[i].a+1,s[i].b,s[i].w);
		insert(1,1,cnt,s[i].a,query(1,1,cnt,1,min(s[i].a,s[i].b))+s[i].w);
	}
	printf("%lld",tre[1]);
	return 0;
}

T3 最小距離

解題思路

思路極簡。。

多源最短路,同時記錄每一個點的最小值來自於哪一個特殊點,跑一下 Dij

接下來列舉列舉每一條邊,用這條邊的兩個端點的兩距離個最小值與邊權之和更新兩個特殊點的答案就好了。

然後就沒有然後了。。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=2e5+10;
int n,m,p,s[N],dis[N],ans[N],pre[N];
bool vis[N];
int tot=1,head[N],nxt[N<<1],ver[N<<1],edge[N<<1];
struct Road
{
	int l,r,val;
}pat[N];
priority_queue<pair<int,int> > q;
void add(int x,int y,int val)
{
	ver[++tot]=y;
	edge[tot]=val;
	nxt[tot]=head[x];
	head[x]=tot;
}
void Dij()
{
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=p;i++)
	{
		pre[s[i]]=s[i];
		dis[s[i]]=0;
		q.push(make_pair(0,s[i]));
	}
	while(!q.empty())
	{
		int x=q.top().second;	q.pop();
		if(vis[x])	continue;
		vis[x]=true;
		for(int i=head[x];i;i=nxt[i])
		{
			int to=ver[i];
			if(dis[x]+edge[i]<dis[to])
			{
				pre[to]=pre[x];
				dis[to]=dis[x]+edge[i];
				q.push(make_pair(-dis[to],to));
			}
		}
	}
}
signed main()
{
	n=read();	m=read();	p=read();
	for(int i=1;i<=p;i++)	s[i]=read();
	memset(ans,0x3f,sizeof(ans));
	for(int i=1,x,y,val;i<=m;i++)
	{
		pat[i].l=x=read();
		pat[i].r=y=read();
		pat[i].val=val=read();
		add(x,y,val);
		add(y,x,val);
	}
	Dij();
	for(int i=1;i<=m;i++)
	{
		int x=pat[i].l,y=pat[i].r;
		if(pre[x]==pre[y])	continue;
		ans[pre[x]]=min(ans[pre[x]],dis[x]+dis[y]+pat[i].val);
		ans[pre[y]]=min(ans[pre[y]],dis[x]+dis[y]+pat[i].val);
	}
	for(int i=1;i<=p;i++)
		printf("%lld ",ans[s[i]]);
	return 0;
}

T4 真相

解題思路

思路來自 AaMuXiiiiii 。。

記錄每一個 \(\$\) 的位置以及所代表的值。

分別列舉所有的值,然後逆推回去看真話的數量是否一直。

如果都不成立的話,在判斷一下說的都是假話的話是否可行。

如果沒有 \(\$\) 的話直接分類討論第一個是否是真話是否合法就好了。

程式碼實現細節較多,陣列清零不可以 memset 會 TLE

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e5+10;
int T,n,s[N],t[N],pre[N][2],ans[N][2];
map<int,bool> b;
vector<int> pos,v;
string opt;
void clear()
{
	b.clear();
	for(int i=0;i<=n;i++)	pre[i][0]=pre[i][1]=ans[i][0]=ans[i][1]=0;
	vector<int>().swap(pos);	vector<int>().swap(v);
}
void solve()
{
	n=read();	int flag=0,maxn=0;
	for(int i=1;i<=n;i++)
	{
		cin>>opt;
		if(opt[0]=='$')	scanf("%lld",&s[i]),flag=i;
		if(opt[0]=='+')	s[i]=-1;
		if(opt[0]=='-')	s[i]=-2;
		maxn=max(maxn,s[i]);
	}
	for(int i=1;i<=n-flag;i++)	t[i]=s[i+flag];
	for(int i=1;i<=flag;i++)	t[i+n-flag]=s[i];
	memcpy(s,t,sizeof(t));
	if(flag)
	{
		for(int i=1;i<=n;i++)
			if(s[i]>=0)
			{
				pos.push_back(i);
				if(!b[s[i]]){b[s[i]]=true;v.push_back(s[i]);}
			}
		for(int i=n;i>=1;i--)
		{
			if(s[i]<0)	continue;
			int las=true;
			pre[i][1]=1;
			for(int j=i-1;j>=1;j--)
			{
				if(s[j]>=0)	break;
				if(s[j]==-1)
				{
					if(las)	pre[i][1]++,las=true;
					else	las=false;
				}
				else
				{
					if(!las)	pre[i][1]++,las=true;
					else	las=false;
				}
			}
			las=false;
			for(int j=i-1;j>=1;j--)
			{
				if(s[j]>=0)	break;
				if(s[j]==-1)
				{
					if(las)	pre[i][0]++,las=true;
					else	las=false;
				}
				else
				{
					if(!las)	pre[i][0]++,las=true;
					else	las=false;
				}
			}
		}
		int all=0;
		for(int i=0;i<pos.size();i++)
		{
			ans[s[pos[i]]][0]+=pre[pos[i]][0];
			ans[s[pos[i]]][1]+=pre[pos[i]][1];
		}
		for(int i=0;i<v.size();i++)
			all+=ans[v[i]][0];
		for(int i=0;i<v.size();i++)
			if(ans[v[i]][1]-ans[v[i]][0]+all==v[i])
			{
				printf("consistent\n");
				clear();
				return ;
			}
		int sum=0;
		for(int i=0;i<pos.size();i++)
			sum+=pre[pos[i]][0];
		for(int i=0;i<v.size();i++)
			if(sum==v[i])
			{
				printf("inconsistent\n");
				clear();
				return ;
			}
		printf("consistent\n");
		clear();
		return ;
	}
	bool las=true;
	for(int i=n-1;i>=1;i--)
		if(s[i]==-2)
			las^=1;
	if(las&&s[n]==-1)
	{
		printf("consistent\n");
		return ;
	}
	if(!las&&s[n]==-2)
	{
		printf("consistent\n");
		return ;
	}
	las=false;
	for(int i=n-1;i>=1;i--)
		if(s[i]==-2)
			las^=1;
	if(las&&s[n]==-2)
	{
		printf("consistent\n");
		return ;
	}
	if(!las&&s[n]==-1)
	{
		printf("consistent\n");
		return ;
	}
	printf("inconsistent\n");
}
signed main()
{
	T=read();
	while(T--)	solve();
	return 0;
}

相關文章