20240502比赛总结

[NOIP2017 提高组] 时间复杂度

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

按题意模拟即可

时间复杂度的计算方式是:

  1. 常数->常数 O(1)

  2. 常数->n O(n)

  3. n->n O(1)

就是细节很多,也不会算时间复杂度,挂成了40

代码:

#include<cstdio>
#include<iostream>
#include<string>
#include<map>
#include<algorithm>
using namespace std;
int T,n,tim,st[105],top,ax[105],ay[105];
string t,name[105];
bool use[30];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		cin>>t;
		tim=0;
		if(t!="O(1)")
		{
			for(int i=4;i<=t.size()-2;i++)
			{
				tim=tim*10+t[i]-'0';
			}
		}
		int ans=0,cnt=0,fl=0;
		for(int i=1;i<=n;i++)
		{
			ax[i]=ay[i]=0;
			string opt;
			cin>>opt;
			if(opt[0]=='F')
			{
				string x,y;
				cin>>name[i]>>x>>y;
				st[++top]=i;
				int tmp=name[i][0]-'a';
				if(use[tmp])
				{
					fl=1000;
					continue;
				}
				if(fl) continue;
				use[tmp]=1;
				int a=0,b=0;
				if(x[0]=='n') a=200;
				else
				{
					for(int i=0;i<x.size();i++) a=a*10+x[i]-'0';
				}
				if(y[0]=='n') b=200;
				else
				{
					for(int i=0;i<y.size();i++) b=b*10+y[i]-'0';
				}
				if(a>b)
				{
					fl=i;
					continue;
				}
				if(b>a&&b==200) cnt++;
				ans=max(ans,cnt);
				ax[i]=a,ay[i]=b;
			}
			else
			{
				if(top==0)
				{
					fl=1000;
					continue;
				}
				int x=st[top];
				top--;
				int tmp=name[x][0]-'a';
				use[tmp]=0;
				if(fl==x) fl=0;
				if(ax[x]<ay[x]&&ay[x]==200&&!fl) cnt--;
			}
		//	printf("%d %d\n",top,cnt);
		}
		if(fl==1000) printf("ERR\n");
		else if(top) printf("ERR\n");
		else if(ans==tim) printf("Yes\n");
		else printf("No\n");
		for(int i=0;i<=26;i++)
		{
			use[i]=0;
		}
		top=0;
	}
	return 0;
}

T2 [CSP-S2019] Emiya 家今天的饭

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

很明显,每种烹饪方式只会做一道菜

从最基本的考虑,如果只有2种食材,设\(dp_{i,j,k}\)表示当前是第i种烹饪方式,第1种食材用了j次,第二种食材用了k次,式子:

\[dp_{i,j,k}=dp_{i-1,j,k}+dp_{i-1,j-1,k}\times a_{i,j}+dp_{i-1,j,k-1}\times a_{i,k} \]

推广,记\(sum_i=\sum_{j=1}^m a_{i,j}\)

\(dp_{i,j,k}\)表示目前是第i种烹饪方法,食材x用了j次,其余食材用了k次,式子:

\[dp_{i,j,k}=dp_{i-1,j,k}+dp_{i-1,j-1,k}\times a_{i,x}+dp_{i-1,j,k-1}\times (sum_i-a_{i,x}) \]

直接枚举x求解即可,时间复杂度\(O(n^3m)\)

考虑继续优化,可以发现,我们只关心差值而不关心具体的值,所以只用记录x的使用次数与其他食材的使用次数的差即可

\[dp_{i,j,k}=dp_{i-1,j,k}+dp_{i-1,j+1}\times a_{i,x}+dp_{i-1,j-1}\times (sum_i-a_{i,x}) \]

时间复杂度\(O(n^2m)\)

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int p=998244353;
int n,m;
ll a[105][2005],sum[105],dp[205][505];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%lld",&a[i][j]);
			sum[i]+=a[i][j];
			sum[i]%=p;
		}
	}
	ll ans=1;
	for(int i=1;i<=n;i++) ans=ans*(sum[i]+1)%p;
	ans=(ans-1+p)%p;
	for(int j=1;j<=m;j++)
	{
		memset(dp,0,sizeof(dp));
		dp[1][n+10]=1;
		dp[1][n+11]=a[1][j];
		dp[1][n+9]=(sum[1]-a[1][j]+p)%p;
		for(int i=2;i<=n;i++)
		{
			for(int k=-n;k<=n;k++)
			{
				int l=k+n+10;
				dp[i][l]=dp[i-1][l]+a[i][j]*dp[i-1][l-1]%p+(sum[i]+p-a[i][j])%p*dp[i-1][l+1]%p;
				dp[i][l]%=p;
			}
		}
		for(int k=1;k<=n;k++)
		{
			int l=k+n+10;
			ans=(ans+p-dp[n][l])%p;
		}
	}
	printf("%lld",ans);
	return 0;
}

T3 [yLOI2020] 凉凉

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

n<=14,考虑状压

\(dp_{i,s}\)表示目前是第i层,放好的地铁的状态为s

显然,转移方程为:\(dp_{i,s}=\min_{st\in s} sum+dp_{i-1,s\ xor\ st}\)

时间复杂度\(O(n^2 4^n)\)

考虑优化,可以使用一些技巧性的枚举方式:for(int st=s;st;st=((st-1)&s))

时间复杂度O(能过)

代码:

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
int n,m,s1[15];
ll a[15][100005],sum[15][15],dp[15][16400];
bool vis[15][100005],v[15][15];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%lld",&a[i][j]);
		}
	}
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		for(int j=1;j<=x;j++)
		{
			int y;
			scanf("%d",&y);
			for(int k=1;k<=n;k++)
			{
				sum[i][k]+=a[k][y];
				if(vis[k][y])
				{
					v[i][k]=v[k][i]=1;
				}
			}
			vis[i][y]=1;
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=n;j>0;j--)
		{
			s1[i]=s1[i]<<1|v[i][j];
		}
	}
	int tmp=(1<<n)-1;
	for(int i=0;i<=n;i++)
	{
		for(int j=0;j<=tmp;j++)
		{
			dp[i][j]=1e18;
		}
	}
	dp[0][0]=0;
	for(int i=1;i<=n;i++)
	{
		for(int s=0;s<=tmp;s++)
		{
			dp[i][s]=dp[i-1][s];
			for(int st=s;st;st=((st-1)&s))
			{
				ll res=0,fl=0;
				for(int j=1;j<=n;j++)
				{
					if(((1<<(j-1))&st)!=0)
					{
						if(st&s1[j])
						{
							fl=1;
							break;
						}
						res+=sum[j][i];
					}
				}
				if(!fl) dp[i][s]=min(dp[i][s],res+dp[i-1][s^st]);
			//	printf("%d %d %d %lld %lld\n",i,s,st,res,dp[i][s|st]);
			}
		}
	}
	printf("%lld",dp[n][tmp]);
	return 0;
}

T4 [联合省选 2020 A]树

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

用trie树暴力维护,在每个节点合并答案即可

代码:

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
#define N 550005
int n,a[N],edgenum,head[N];
struct edge{
	int to,nxt;
}e[N<<1];
void add_edge(int u,int v)
{
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	head[u]=edgenum;
}
struct node{
	int cnt,dep,sum,ch[2];
}tr[N*21];
int rt[N],tot;
void pushup(int id)
{
	int ls=tr[id].ch[0],rs=tr[id].ch[1];
	tr[id].sum=tr[ls].sum^tr[rs].sum^((tr[rs].cnt&1)<<tr[id].dep);
}
void insert(int &id,int u,int d)
{
	if(!id)
	{
		id=++tot;
		tr[id].dep=d;
	}
	tr[id].cnt++;
	if(d>20) return;
	int tmp=((u>>d)&1);
	insert(tr[id].ch[tmp],u,d+1);
	pushup(id);
}
int merge(int x,int y)
{
	if(!x||!y) return x+y;
	tr[x].cnt+=tr[y].cnt;
	tr[x].ch[0]=merge(tr[x].ch[0],tr[y].ch[0]);
	tr[x].ch[1]=merge(tr[x].ch[1],tr[y].ch[1]);
	pushup(x);
	return x;
}
void add(int id)
{
	if(!id||tr[id].dep>20) return;
	add(tr[id].ch[1]);
	swap(tr[id].ch[0],tr[id].ch[1]);
	pushup(id);
}
int ans[N];
void dfs(int u,int fa)
{
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa) continue;
		dfs(v,u);
		rt[u]=merge(rt[u],rt[v]);
	}
	add(rt[u]);
	insert(rt[u],a[u],0);
	ans[u]=tr[rt[u]].sum;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=2;i<=n;i++)
	{
		int fa;
		scanf("%d",&fa);
		add_edge(i,fa);
		add_edge(fa,i);
	}
	dfs(1,0);
	ll res=0;
	for(int i=1;i<=n;i++)
	{
		res+=1ll*ans[i];
	//	printf("%d ",ans[i]);
	}
	printf("%lld",res);
	return 0;
}
posted @ 2024-05-02 15:10  wangsiqi2010916  阅读(17)  评论(1编辑  收藏  举报