20240503比赛总结

T1 [CF1279C]Stack of Presents

https://gxyzoj.com/d/hzoj/p/3686

数据出锅了,100->40

按题意模拟即可,可以发现,最优情况下,一定是将取出的数按后面的拿的顺序排序,O(1)取出,而在取之前未排序的,则需要花2k+1的时间排序并取出

代码:

#include<cstdio>
#define ll long long
using namespace std;
int T,n,m,a[100005],b[100005],vis[100005];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			vis[a[i]]=i;
		}
		for(int i=1;i<=m;i++)
		{
			scanf("%d",&b[i]);
			b[i]=vis[b[i]];
		}
		ll ans=0;
		int id=0;
		for(int i=1;i<=m;i++)
		{
			if(b[i]<id) ans++;
			else
			{
				ans=ans+1ll*2*(b[i]-i)+1;
				id=b[i];
			}
		//	printf("%d\n",ans);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

T2 [luogu5522]棠梨煎雪

https://gxyzoj.com/d/hzoj/p/3667

树状数组+卡常

没事千万别写线段树!!!

可以发现,如果同一位上既有0,又有1,则这个区间必然无解,而如果所有的都是?,则这一位有两种情况,乘法原理求解即可

如何判断0与1的情况?

直接用60个树状数组,记录每一位0和1的情况,然后求和即可

代码:

#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
int n,m,q;
string s[100010];
int lowbit(int x)
{
	return x & (-x);
}
struct tree{
	int a[100010];
	void add(int x,int val)
	{
		while(x<=m)
		{
			a[x]+=val;
			x+=lowbit(x);
		}
	}
	int query(int x)
	{
		int res=0;
		while(x)
		{
			res+=a[x];
			x-=lowbit(x);
		}
		return res;
	}
}tr[65];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>q;
	for(int i=1;i<=m;i++)
	{
		cin>>s[i];
		for(int j=0;j<n;j++)
		{
			int x=s[i][j]-'0';
			if(s[i][j]!='?')
			tr[j*2+x].add(i,1);
		}
	}
	int ans=0;
	while(q--)
	{
		int opt;
		cin>>opt;
		if(opt==0)
		{
			int l,r;
			cin>>l>>r;
			int sum=1;
			for(int i=0;i<n;i++)
			{
				int x=tr[i*2].query(r)-tr[i*2].query(l-1);
				int y=tr[i*2+1].query(r)-tr[i*2+1].query(l-1);
				if(x!=0&&y!=0)
				{
					sum=0;
					break;
				}
				if(x==0&&y==0) sum<<=1;
			}
			ans^=sum;
		//	cout<<sum<<"\n";
		}
		else
		{
			int x;
			string st;
			cin>>x>>st;
			for(int i=0;i<n;i++)
			{
				int tmp=s[x][i]-'0';
				if(s[x][i]!='?') 
				tr[i*2+tmp].add(x,-1);
				tmp=st[i]-'0';
				if(st[i]!='?')
				tr[i*2+tmp].add(x,1);
			}
			s[x]=st;
		}
	}
	cout<<ans;
	return 0;
}

T3 [luogu1174]打砖块

https://gxyzoj.com/d/hzoj/p/3685

先考虑前50%的数据,设\(dp_{i,j}\)表示前i列打j个的子弹的最大值,显然方程为

\[dp_{i,j}=max(dp_{i,j},dp_{i-1,j-l}+sum_{l,i}) \]

对于满分,存在一种情况,就是程序认为打Y所花的子弹为0,所以在子弹数为0的时候会继续加上所有Y的权值

所以这里可以记\(dn_{i,j}\)表示第i列的第j个子弹打在N上的值,\(dy_{i,j}\)表示第i列的第j个子弹打在Y上的值

\(fn_{i,j}\)表示第i列共打j个子弹且最后打在N上的最大值,\(fy_{i,j}\)表示第i列共打j个子弹且最后打在Y上的最大值

根据以上分析,显然如果要打Y,则前面必然剩下一发子弹,在打完后也必然剩一发子弹,所以转移为:

\[fy_{i,j}=\max (fy_{i-1,j-k}+dy_{i,k}) \]

接下来考虑最后一发打在N上的情况,这里引入一种借子弹的思想

在第x列和第y列最后一个都打到N时,考虑先不打x列的最后一个N,而是将子弹借给第y列打那个N后的Y,再将剩余的那一发子弹还给x去打N,从而获得更多的分

此时就存在两种情况

  1. i给[1,i-1]借,方程:

\[fn_{i,j}=\max (fn_{i,j},fy_{i-1,j-l}+dn_{i,l}) \]

  1. i给[i+1,n]借,方程:

\[fn_{i,j}=\max (fn_{i,j},fn_{i-1,j-l}+dy_{i,l}) \]

答案显然为\(fn_{m,k}\)

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,k,a[205][205],b[205][205];
int dn[205][205],dy[205][205],fn[205][205],fy[205][205];
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&a[i][j]);
			char c;
			cin>>c;
			if(c=='Y') b[i][j]=1;
		}
	}
	for(int j=1;j<=m;j++)
	{
		int tot=0;
		for(int i=n;i>0;i--)
		{
			if(b[i][j]) dy[j][tot]+=a[i][j];
			else
			{
				tot++;
				dn[j][tot]=dy[j][tot-1]+a[i][j];
				dy[j][tot]=dn[j][tot];
			}
		}
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=0;j<=k;j++)
		{
			for(int l=0;l<=min(j,n);l++)
			{
				fy[i][j]=max(fy[i][j],fy[i-1][j-l]+dy[i][l]);
				if(l!=0) fn[i][j]=max(fn[i][j],fy[i-1][j-l]+dn[i][l]);
				if(j!=l) fn[i][j]=max(fn[i][j],fn[i-1][j-l]+dy[i][l]);
			}
		}
	}
	printf("%d",fn[m][k]);
	return 0;
}

T4 「NOIP2015」斗地主

https://gxyzoj.com/d/hzoj/p/1181

暴力dfs,按题意模拟即可

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int T,n,a[25],sum[20],ans;
inline void dfs(int x)
{
//	printf("%d %d\n",x,use);
	//printf("\n");
	if(x>=ans) return; 
	int cnt=0;
	for(int i=3;i<=14;i++)//单顺子 
	{
		if(sum[i]==0) cnt=0;
		else
		{
			cnt++;
			if(cnt>=5)
			{
				for(int j=i-cnt+1;j<=i;j++) sum[j]--;
				dfs(x+1);
				for(int j=i-cnt+1;j<=i;j++) sum[j]++;
			}
		}
	}
	cnt=0;
	for(int i=3;i<=14;i++)//双顺子 
	{
		if(sum[i]<2) cnt=0;
		else
		{
			cnt++;
			if(cnt>=3)
			{
				for(int j=i-cnt+1;j<=i;j++) sum[j]-=2;
				dfs(x+1);
				for(int j=i-cnt+1;j<=i;j++) sum[j]+=2;
			}
		}
	}
	cnt=0;
	for(int i=3;i<=14;i++)//三顺子  
	{
		if(sum[i]<3) cnt=0;
		else
		{
			cnt++;
			if(cnt>=2)
			{
				for(int j=i-cnt+1;j<=i;j++) sum[j]-=3;
				dfs(x+1);
				for(int j=i-cnt+1;j<=i;j++) sum[j]+=3;
			}
		}
	}
	for(int i=2;i<=14;i++)
	{
		if(sum[i]<3) continue;
		sum[i]-=3;
		dfs(x+1);//三张牌 
		for(int j=2;j<=15;j++)//三带一 
		{
			if(!sum[j]||i==j) continue;
			sum[j]--;
			dfs(x+1);
			sum[j]++;
		}
		for(int j=2;j<=14;j++)//三带二 
		{
			if(sum[j]<2||i==j) continue;
			sum[j]-=2;
			dfs(x+1);
			sum[j]+=2;
		}
		sum[i]+=3;
		if(sum[i]<4) continue;
		sum[i]-=4;
		dfs(x+1);//炸弹 
		for(int j=2;j<=15;j++)//四带二 +单牌 
		{
			if(!sum[j]||i==j) continue;
			sum[j]--;
			for(int k=j+1;k<=15;k++)
			{
				if(!sum[k]||k==i||k==j) continue;
				sum[k]--;
				dfs(x+1);
				sum[k]++;
			}
			sum[j]++;
		}
		for(int j=2;j<=14;j++)//四带二 +对牌 
		{
			if(sum[j]<=1||i==j) continue;
			sum[j]-=2;
			for(int k=j+1;k<=14;k++)
			{
				if(sum[k]<2||k==i||k==j) continue;
				sum[k]-=2;
				dfs(x+1);
				sum[k]+=2;
			}
			sum[j]+=2;
		}
		sum[i]+=4;
	}
    int tmp=0;
	for(int i=1;i<=15;i++)
	{
		if(sum[i])tmp++; 
	}
	ans=min(ans,x+tmp);
}
int main()
{
	scanf("%d%d",&T,&n);
	while(T--)
	{
		ans=n;
		for(int i=1;i<=n;i++)
		{
			int b;
			scanf("%d%d",&a[i],&b);
			if(a[i]==0) sum[15]++;
			else if(a[i]==1) sum[14]++;
			else sum[a[i]]++;
		}
	//	for(int i=1;i<=15;i++) printf("%d ",sum[i]);
	//	printf("\n");
		dfs(0);
		printf("%d\n",ans);
		for(int i=2;i<=15;i++) sum[i]=0;
	}
	return 0;
}
posted @ 2024-05-03 21:32  wangsiqi2010916  阅读(25)  评论(1编辑  收藏  举报