CCPC-Wannafly Winter Camp Day4 div2 题解

实力不济,用oeis也只有6题就滚粗了

A.夺宝奇兵

这个题只要想通了就特别简单,因为是往返的路,那么我就当成是两条线路同时走好了,假设每类宝藏的两个宝藏位置是a,b,那么我要从第 i 类宝藏走到第 i+1类宝藏,就有两种走法,(ai->ai+1,bi->bi+1)和(ai->bi+1,bi->ai+1),看哪一种短就走哪一种。

#include
#define ll long long
using namespace std;
const int maxn=1e5+10;
struct node
{
	int x,y;
}a[maxn],b[maxn];
int ss(node t1,node t2)
{
	return abs(t1.x-t2.x)+abs(t1.y-t2.y);
}
int main()
{
	ll ans=0;
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d",&a[i].x,&a[i].y,&b[i].x,&b[i].y);
		if(i==1)continue;
		ans+=min(ss(a[i-1],a[i])+ss(b[i-1],b[i]),ss(a[i-1],b[i])+ss(b[i-1],a[i]));
	}
	ans+=ss(a[n],b[n]);
	printf("%lld\n",ans);
}

C.最小边覆盖

先算好每个点的度数,如果有的点度数为0肯定No,然后枚举边,如果有一条边连接的两点度数同时>1,也是No,其他就Yes。

#include
#define ll long long
using namespace std;
const int maxn=2e5+10;
int d[maxn],u[maxn],v[maxn];
int main()
{
	int n,m,flag=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u[i],&v[i]);
		d[u[i]]++;
		d[v[i]]++;
	}
	for(int i=1;i<=n;i++)
	if(!d[i])flag=1;
	for(int i=1;i<=m;i++)
	{
		if(d[u[i]]>1&&d[v[i]]>1)
		flag=1;
	}
	if(flag)puts("No");
	else puts("Yes");
}

F.小小马

如果从起点走不到终点,那肯定No,如果能的话,我们分析一下,假设当前的点是白点,由于马走日,那么下一个点肯定是黑点,因此如果终点和起点颜色相同,那肯定No,否则Yes

#include
#define ll long long
using namespace std;
int n,m,sx,sy,tx,ty,vis[25][25];
int ss()
{
	if(n>=10&&m>=10)return 1;
	if(n==2)
	{
		int k=abs(sy-ty);
		if(sx==tx)
		{
			if(k%4==0)
			return 1;
			return 0;
		}
		if(k%2==0&&k%4)return 1;
		return 0;
	}
	if(n==3)
	{
		if(m>3)return 1;
		if(sx==2&&sy==2)return 0;
		if(tx==2&&ty==2)return 0;
		return 1;
	}
	return 1;
}
int main()
{
	int t1,t2;
	cin>>n>>m;
	cin>>sx>>sy;
	t1=((sx%2==sy%2)?0:1);
	cin>>tx>>ty;
	t2=((tx%2==ty%2)?0:1);
	if(n>m)
	{
		swap(n,m);
		swap(sx,sy);
		swap(tx,ty); 
	}
	if(!ss())
	{
		puts("No");
		return 0;
	}
	if(t1==t2)puts("No");
	else puts("Yes");
}

G.置置置换

oeis过的这题,dp解法待补....

#include
#define ll long long
using namespace std;
const int inf=1e9,mod=1e9+7;
int E[1005][1005];
ll d[1005];
void init()
{
	for(int i=0;i<=1000;i++)
	for(int j=0;j<=1000;j++)
	E[i][j]=-inf;
}
int dfs(int n,int m)
{
	if(E[n][m]!=-inf)return E[n][m];
	if(m==0&&n==0)return E[n][m]=1;
	if(m==0)return E[n][m]=0;
	E[n][m]=(dfs(n,m-1)+dfs(n-1,n-m))%mod;
	return E[n][m];
}
void sub(ll& x,ll y)
{
	x=(x-y+mod)%mod;
}
void add(ll& x,ll y)
{
	x=(x+y)%mod;
}
int main()
{
	init();
	for(int i=1;i<=1000;i++)
	for(int j=1;j<=1000;j++)
	dfs(i,j);
	d[1]=1,d[2]=1,d[3]=2;
	for(int i=4;i<=1000;i++)
	{
		d[i]=d[i-1]*(i-1)%mod;
		ll res=0;
		for(int j=2;j<=i-2;j++)
		add(res,1ll*(j-1)*E[i-2][i-1-j]%mod);
		sub(d[i],res);
	}
	int n;
	while(~scanf("%d",&n))
	printf("%lld\n",d[n]);
}

I.咆咆咆哮

update:被hack了,数据:3 100 70 1 2 3 4

设d[ i ]为召唤了 i 张a类卡片所能得到的最大值,那么可以看出来,d[ i+1 ]肯定是由d[ i ]的 i 张a类卡片+其他的一张a类卡片推出来,因此我枚举所有在d[ i ]中用的b类卡片 j,假设我要把第 j 张卡片换成a类,假设sum为在d[i]中用的所有bj的总和,那么d[i+1]=max(d[i]-b[j]*i+a[i]+sum-b[j]),找到最优的 j,然后标记第 j 张卡片并且更新sum即可。

由于d[i+1]只和d[ i ]有关,因此我去掉了数组,用变量res代替。

#include
#define ll long long
using namespace std;
int a[1005],b[1005],vis[1005],n;
int main()
{
	ll res=0,ans=0,sum=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i]>>b[i];
		sum+=b[i];
	}
	int p;
	for(int i=1;i<=n;i++)
	{
		p=0;
		ll tmp2=0,tmp;
		for(int j=1;j<=n;j++)
		if(!vis[j])
		{
			tmp=res-1ll*b[j]*(i-1);
			tmp+=a[j]+sum-b[j];
			if(!p)
			{
				p=j;
				tmp2=tmp;
				continue;
			}
			if(tmp>tmp2)
			{
				tmp2=tmp;
				p=j;
			}
		}
		vis[p]=1;
		sum-=b[p];
		res=tmp2;
		ans=max(ans,res);
	}
	printf("%lld\n",ans);
}

update:我这个思路是错的,d[ i+1 ]不一定只能由d[ i ]推出来,正解应该是枚举 i 个a类卡片,并且贪心取得最优值,假设每类卡片都是 b 类,如果我要把第 j 张卡片换成 a 类,那么总权值就会+a[ j ] - b[ j ]*i,把这个值记为s,我们分别记录这 n 张卡片的s值,按照从大到小排序,前 i 张卡片取a类,后面的卡片取b类求值就好了。

#include
#define ll long long
using namespace std;
struct node
{
	ll a,b,s;
	bool operator<(const node&t)const
	{
		return s>t.s;
	}
}c[1005];
ll ans;
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>c[i].a>>c[i].b;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		c[j].s=c[j].a-c[j].b*i;
		sort(c+1,c+1+n);
		ll res=0;
		for(int j=1;j<=n;j++)
		if(j<=i)
		res+=c[j].a;
		else res+=c[j].b*i;
		ans=max(ans,res); 
	}
	cout<

K.两条路径

这个题其实巨水,两条路线交集只能是x,并且要并集总权值最大,我只要在以x为根节点的树找到四条权值最大的不一样的路线,把他们加起来就是答案。

#include
#define ll long long
using namespace std;
const int maxn=1e5+19;
vectorG[maxn];
int vis[maxn],a[maxn],f[maxn],p;
ll ans,mx;
void dfs(int u,int fa,ll res)
{
	f[u]=fa;
	if(res>mx)
	{
		mx=res;
		p=u;
	}
	for(int i=0;is;
	if(ans<0)printf("-"),ans=-ans;
	while(ans!=0)
	{
		if(ans&1)s.push(1);
		else s.push(0);
		ans/=2;
	}
	while(!s.empty())
	printf("%d",s.top()),s.pop();
	puts("");
}
int main()
{
	int n,u,v;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]==0)continue;
		int tmp=1<<(abs(a[i])-1);
		if(a[i]<0)a[i]=-tmp;
		else a[i]=tmp; 
	}
	for(int i=1;i

 

你可能感兴趣的:(比赛----其他比赛题解)