noip33

T1

第一个猎人死的轮数等于在1号猎人之前死的猎人数+1,如果当前这个人没死,那么他死在一号猎人之前的概率为 \(\frac{w_{i}}{w_{1}+w_{i}}\),因为每死一个就会造成1的贡献,所以概率就是期望。最后答案记得+1。

Code
#include<cstdio>
#define MAX 100010
#define re register
#define int long long
namespace OMA
{
	int n,w[MAX];
	const int p = 998244353;
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			int w=1; s=0; char ch=getchar();
			while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
			while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
			return s*=w,*this;
		}
	}cin;
	inline int quickpow(int a,int b)
	{
		int ans = 1;
		while(b)
		{
			if(b&1)
			{ ans = ans*a%p; }
			a = a*a%p;
			b >>= 1;
		}
		return ans;
	}
	int ans;
	signed main()
	{
		cin >> n;
		for(re int i=1; i<=n; i++)
		{ cin >> w[i]; }
		for(re int i=2; i<=n; i++)
		{ (ans += w[i]*quickpow(w[1]+w[i],p-2)%p) %= p; }
		printf("%lld\n",ans+1);
		return 0;
	}
}
signed main()
{ return OMA::main(); }

T2

再次是读题死亡,依旧是加粗了没看见。

注意符咒是对每个1进行操作,考试时看错题,样例还一下就过了QAQ。

正解:

线段树合并/启发式合并

首先,肯定是先把法术用了,再去用符咒,考虑用完法术后的序列。

\(l\) 为左边第一个1的位置,\(r\) 为右边第一个1的位置,\(xam\) 为中间最长的一段连续的0的长度,则答案为 \(\max(m-r+l-1,xam)\)\(Push\;up\) 的时候维护一下就好。

那如何判断无解情况? 只需判断

注意一些坑点:比如一个城池可以掌握多种法术。

Code
#include<cstdio>
#define MAX 100010
#define re register
namespace OMA
{
	int n,m,q;
	int ans[MAX];
	struct graph
	{
		int next;
		int to;
	}edge[MAX];
	int cnt=1,head[MAX];
	inline void add(int u,int v)
	{ edge[++cnt] = (graph){head[u],v},head[u] = cnt; }
	struct Segmnet_Tree
	{
		int tot;
		struct TREE
		{
			int l,r;
			int xam;
			int size;
			int ls,rs;
			inline int len()
			{ return m-r+l-1; }
		}st[MAX<<5];
		int root[MAX];
		#define ls(p) st[p].ls
		#define rs(p) st[p].rs
		inline int max(int a,int b)
		{ return a>b?a:b; }
		inline void Push_up(int p)
		{
			st[p].size = st[ls(p)].size+st[rs(p)].size;
			st[p].xam = max(st[ls(p)].xam,st[rs(p)].xam);
			if(st[ls(p)].r&&st[rs(p)].l)
			{ st[p].xam = max(st[p].xam,st[rs(p)].l-st[ls(p)].r-1); }
			if(st[ls(p)].l)
			{ st[p].l = st[ls(p)].l; }
			else
			{ st[p].l = st[rs(p)].l; }
			if(st[rs(p)].r)
			{ st[p].r = st[rs(p)].r; }
			else
			{ st[p].r = st[ls(p)].r; }
		}
		inline void insert(int &p,int l,int r,int x)
		{
			p = !p?++tot:p;
			if(l==r)
			{ st[p].size = 1,st[p].l = st[p].r = l; return ; }
			int mid = (l+r)>>1;
			if(x<=mid)
			{ insert(ls(p),l,mid,x); }
			else
			{ insert(rs(p),mid+1,r,x); }
			Push_up(p);
		}
		inline int merge(int p1,int p2,int l,int r)
		{
			if(!p1||!p2)
			{ return p1|p2; }
			if(l==r)
			{ return p1; }
			int mid = (l+r)>>1;
			ls(p1) = merge(ls(p1),ls(p2),l,mid);
			rs(p1) = merge(rs(p1),rs(p2),mid+1,r);
			Push_up(p1);
			return p1;
		}
		inline void dfs(int u)
		{
			for(re int i=head[u],v; i; i=edge[i].next)
			{
				v = edge[i].to; dfs(v);
				root[u] = merge(root[u],root[v],1,m);
			}
			ans[u] = st[root[u]].size?max(st[root[u]].xam,st[root[u]].len()):-1;
		}
	}Tree;
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			int w=1; s=0; char ch=getchar();
			while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
			while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
			return s*=w,*this;
		}
	}cin;
	signed main()
	{
		cin >> n >> m >> q;
		for(re int i=1,u,v; i<=n-1; i++)
		{ cin >> u >> v; add(u,v); }
		for(re int i=1,u,p; i<=q; i++)
		{ cin >> u >> p; Tree.insert(Tree.root[u],1,m,p); }
		Tree.dfs(1);
		for(re int i=1; i<=n; i++)
		{ printf("%d\n",ans[i]); }
		return 0;
	}
}
signed main()
{ return OMA::main(); }

T3

完全图骗分 \(ans=(n-2)\times w\),20pts。

再加上一些乱搞,+10pts。

然而大帝 "乱搞" 有80pts orz。

正解:

神仙状压。

\(dp_{i,j}\) 表示当前所选集合为 \(i\) ,当前链的结尾为 \(j\),状态转移考虑两种情况:

  1. 在当前链的结尾新加入节点。

  2. 给当前链新增个集合/链上去。

预处理出每一种链边的总权值,某个链向另外一个链连边的权值。

或者说叫联通块?

关于状压枚举子集

\(S1\) 表示当前枚举的子集,\(S2\cup S1=S\)\(S\) 为全集。

for(re int s1=s; s1; s1=(s1-1)&s)
{ s2 = s^s1; }
Code
#include<cstdio>
#include<cstring>
#define re register
const int MAX = 1<<16;
namespace OMA
{
	int n,m,top;
	int dp[MAX][16],sum[MAX];
	int dis1[16][16],dis2[MAX][16];
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			int w=1; s=0; char ch=getchar();
			while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
			while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
			return s*=w,*this;
		}
	}cin;
	inline int max(int a,int b)
	{ return a>b?a:b; }
	signed main()
	{
		cin >> n >> m;
		for(re int i=1,u,v,w; i<=m; i++)
		{
			cin >> u >> v >> w;
			dis1[u][v] = w,dis1[v][u] = w;
		}
		top = (1<<n)-1;
		for(re int i=1; i<=top; i++)
		{
			for(re int j=1; j<=n; j++)
			{
				for(re int k=j+1; k<=n; k++)
				{
					if(((i>>(j-1))&1) and ((i>>(k-1))&1))
					{ sum[i] += dis1[j][k]; }
				}
			}
		}
		for(re int i=1; i<=top; i++)
		{
			for(re int j=1; j<=n; j++)
			{
				if((i>>(j-1))&1)
				{
					for(re int k=1; k<=n; k++)
					{
						if(not ((i>>(k-1))&1) and dis1[j][k])
						{ dis2[i][k] += dis1[j][k]; }
					}
				}
			}
		}
		memset(dp,128,sizeof(dp)); dp[1][1] = 0;
		for(re int i=1; i<=top; i++)
		{
			for(re int j=1; j<=n; j++)
			{
				if(dp[i][j]<0)
				{ continue ; }
				for(re int k=1; k<=n; k++)
				{
					if((not (i>>(k-1))&1) and dis1[j][k])
					{ dp[i|(1<<(k-1))][k] = max(dp[i|(1<<(k-1))][k],dp[i][j]+dis1[j][k]); }
				}
				int l = i^top;
				for(re int k=l; k; k=(k-1)&l)
				{ dp[i|k][j] = max(dp[i|k][j],dp[i][j]+sum[k]+dis2[k][j]); }
			}
		}
		printf("%d\n",sum[top]-dp[top][n]);
		return 0;
	}
}
signed main()
{ return OMA::main(); }
posted @ 2021-08-08 20:06  -OMA-  阅读(79)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end