博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

7.30 正睿暑期集训营 A班训练赛


2018.7.30 正睿暑期集训营 A班训练赛

时间:8:00~13:00
期望得分:100+5+5
实际得分:100+5+0

比赛链接

很多人Hash被卡了(写得丑怪谁呢),水了个A班前10 2333.

T1 A.蔡老板分果子(Hash)

题目链接

对下标集合进行Hash。只需要为每个下标随机一个权值即可。
不放心写了双哈希,其实单哈希就够了。异或或者加,随机权值或设定都行。
一个赋随机unsigned long long权值方式:for(int j=1; j<=5; ++j) val[i]=val[i]<<15^rand();

#include <map>
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define base1 (786433)
#define base2 (1572869)
typedef long long LL;
typedef unsigned long long ull;
const int N=2e5+5;
const ull P1[7]={31,193,331,769,12289};
const ull P2[7]={131,97,53,389,49157};

int n;
long long Ans;
ull pw1[N],pw2[N],sum1[N],sum2[N];
std::map<ull,int> vis1,vis2;
char IN[MAXIN],*SS=IN,*TT=IN;
struct Edge
{
	int Enum,H[N],nxt[N<<1],to[N<<1];
	inline void AddEdge(int u,int v)
	{
		to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
		to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
	}
}T1,T2;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
void DFS1(int x,int f)
{
	sum1[x]=pw1[x]*base1, sum2[x]=pw2[x]*base2;
	for(int v,i=T1.H[x]; i; i=T1.nxt[i])
		if((v=T1.to[i])!=f) DFS1(v,x), sum1[x]+=sum1[v], sum2[x]+=sum2[v];
	++vis1[sum1[x]], ++vis2[sum2[x]];
}
void DFS2(int x,int f)
{
	static std::map<ull,int>::iterator it1,it2;

	sum1[x]=pw1[x]*base1, sum2[x]=pw2[x]*base2;
	for(int v,i=T2.H[x]; i; i=T2.nxt[i])
		if((v=T2.to[i])!=f) DFS2(v,x), sum1[x]+=sum1[v], sum2[x]+=sum2[v];
	if((it1=vis1.find(sum1[x]))!=vis1.end() && (it2=vis2.find(sum2[x]))!=vis2.end())
		Ans+=(LL)std::min(it1->second,it2->second);//, printf("Add! %d:%d\n",x,it->second);
}

int main()
{
//	freopen("a3.in","r",stdin);

	n=read(); pw1[0]=pw2[0]=1;
	for(int i=1; i<=n; ++i) pw1[i]=pw1[i-1]*base1+P1[i%5]*i, pw2[i]=pw2[i-1]*base2+P2[i%5]*i;;
	for(int i=1; i<n; ++i) T1.AddEdge(read(),read());
	for(int i=1; i<n; ++i) T2.AddEdge(read(),read());
	DFS1(1,1), --vis1[sum1[1]], --vis2[sum2[1]], DFS2(1,1);
	printf("%lld\n",Ans);

	return 0;
}

T2 B.蔡老板送外卖(并查集 最小生成树)

题目链接
我们要求删掉i后的最小生成树的最大边的权值。我们对所有的点一起考虑。
先求出最小生成树。考虑删掉每个点后其余非树边能对它的贡献。
我们存下非树边,对于边(u,v),w=LCA(u,v),假设dep[u]>dep[v],那么有两种情况:
1.w==v,暂且称这条边为返祖边,它会对u->v(w)(不包括u,v)这条链的点贡献合并上下两个连通块的边。
2.w!=v,称这条边为横边,同理会对u->w,v->w(不包括u,v)上的点贡献合并u,v所在连通块的边。
如果把边从小到大插入,比如一些点被较小的返祖边覆盖过,那么更大的返祖边对它显然没什么用。我们跳过之前更新过的一条链,这可以用并查集。
具体是对于当前节点x,x=Get_fa(x),然后合并x与fa[x],再找到fa[x]所在集合的代表节点,合并其与其父节点...
对fa[]更新这条边的答案,u,v不会被计算,但是会被合并,但是没问题,u,v作为父节点会被更新一次。
不清楚的话,画个图就好了。
判断无解可以记每个点被合并连通块的次数就可以了。
对于返祖边有一次次数贡献;对于横边,我们更新完链后将u,v合并,若u,v之前未被合并则对w有一次次数贡献。
每条返祖边只被枚举一次,每条横边相当于只在LCA处枚举一次。复杂度O(mlogn*α(n))。

要注意并查集每次要把深度高的点的fa[]指向深度低的点;跳fa的时候是树上的不要和并查集的混了。

//4297ms	76584kb
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=1e6+5;

int n,m,Enum,H[N],to[N<<1],nxt[N<<1],cnt,dgr[N],fa[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Edge
{
	int fr,to,val;
	bool operator <(const Edge &x)const{
		return val<x.val;
	}
}e[N],g[N];

#define AddEdge(u,v) to[++Enum]=v,nxt[Enum]=H[u],H[u]=Enum,to[++Enum]=u,nxt[Enum]=H[v],H[v]=Enum
inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
int Get_fa(int x){
	return x==fa[x]?x:fa[x]=Get_fa(fa[x]);
}
void Merge(int u,int v){
	if(Get_fa(u)!=Get_fa(v)) fa[fa[u]]=fa[v];
}
int Kruskal()
{
	std::sort(e+1,e+1+m);
	for(int i=1; i<=n; ++i) fa[i]=i;
	int res=0,k=1;
	for(int i=1,u,v; i<=m; ++i)
	{
		if(Get_fa(u=e[i].fr)==Get_fa(v=e[i].to)) {g[++cnt]=e[i]; continue;}
		++k, ++dgr[u], ++dgr[v], AddEdge(u,v);
		fa[fa[u]]=fa[v], res=std::max(res,e[i].val);
//		if(++k==n) return res;//g
	}
	return k==n?res:-1;
} 
namespace HLD//Heavy-Light Decomposition
{
	int fa[N],sz[N],son[N],dep[N],top[N];
	inline int LCA(int u,int v)
	{
		while(top[u]!=top[v]) dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
		return dep[u]>dep[v]?v:u;
	}
	void DFS1(int x)
	{
		int mx=0; sz[x]=1;
		for(int i=H[x],v; i; i=nxt[i])
			if((v=to[i])!=fa[x])
			{
				fa[v]=x, dep[v]=dep[x]+1, DFS1(v), sz[x]+=sz[v];
				if(sz[v]>mx) mx=sz[v], son[x]=v;
			}
	}
	void DFS2(int x,int tp)
	{
		top[x]=tp;
		if(son[x])
		{
			DFS2(son[x],tp);
			for(int i=H[x],v; i; i=nxt[i])
				if((v=to[i])!=fa[x] && v!=son[x]) DFS2(v,v);
		}
	}
}
using HLD::dep;
using HLD::LCA;

int main()
{
	n=read(), m=read();
	for(int i=1; i<=m; ++i) e[i]=(Edge){read(),read(),read()};
	int MinTree=Kruskal();
	if(MinTree==-1) return printf("%d\n",-n),0;

	HLD::DFS1(1), HLD::DFS2(1,1);

	static int tot[N],Ans[N];
	for(int i=1; i<=n; ++i) fa[i]=i;
	for(int i=1,u,v,tu,tv,w,val; i<=cnt; ++i)
	{
		if(dep[u=g[i].fr]<dep[v=g[i].to]) std::swap(u,v);
		w=LCA(u,v), val=g[i].val;
		if(v==w)
		{
			u=Get_fa(u);
			for(int p=HLD::fa[u]; dep[p]>dep[v]; u=Get_fa(u), p=HLD::fa[u])
				Ans[p]=val, ++tot[p], Merge(u,p);
		}
		else
		{
			u=Get_fa(tu=u), v=Get_fa(tv=v);
			for(int p=HLD::fa[u]; dep[p]>dep[w]; u=Get_fa(u), p=HLD::fa[u])
				Ans[p]=val, ++tot[p], Merge(u,p);
			for(int p=HLD::fa[v]; dep[p]>dep[w]; v=Get_fa(v), p=HLD::fa[v])
				Ans[p]=val, ++tot[p], Merge(v,p);
			if((u=Get_fa(tu))==(v=Get_fa(tv))) continue;
			Ans[w]=val, ++tot[w];
			if(dep[u]<dep[v]) std::swap(u,v);
			fa[fa[u]]=fa[v];
		}
	}
	long long res=0;
	for(int i=1; i<=n; ++i)
		res += dgr[i]==1?MinTree:(tot[i]<dgr[i]-1?-1:std::max(MinTree,Ans[i]));
	printf("%lld\n",res);

	return 0;
}

T3 C.蔡老板学数学(DP NTT)

题目链接
???

考试代码

T2

/*
对于每个点i,求其MST上的最大边。其MST上i的度数只能为1。
对于每个连通块单独求其MST,当根节点不在其中时显然它就可以是答案。然而这没啥用,很容易有个大连通块。应该是二档部分分?
根节点只能连出一条,它的边只有把它与连通块连起来,而不能再帮助其它点并入连通块、so直接选其最小边即可。那么去掉根节点做个MST。
即求:i=1~n,去掉点i的最小生成树的最大边。只求和,算贡献?
LCT!不对啊,cut掉i的边 还是要枚举m条连起来。。而且忘了怎么写了。我还是咸鱼吧。
瞎写的 没调完 也没啥意思。
*/
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <algorithm>
#define gc() getchar()
//#define MAXIN 400000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define Vec std::vector<int>
typedef long long LL;
const int N=1e6+5,S=4*N,INF=0x3f3f3f3f;

int n,m,Enum,H[N],nxt[N<<1],to[N<<1],len[N<<1],tmp[N<<1],mn[N],Index,low[N],dfn[N],top,sk[N],fa[N],scnt,val[N],ff[N],Ans[N];
Vec scc[N],edge[N],bel[N];
//char IN[MAXIN],*SS=IN,*TT=IN;
struct Edge
{
	int fr,to,val;
	Edge() {}
	Edge(int f,int t,int v):fr(f),to(t),val(v) {}
	bool operator <(const Edge &a)const{
		return val<a.val;
	}
}e[N];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
inline void AddEdge(int w,int u,int v)
{
	mn[v]=std::min(mn[v],w), to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;
	mn[u]=std::min(mn[u],w), to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, len[Enum]=w;
	e[Enum>>1]=Edge(u,v,w);
}
namespace NoSolution
{
	bool ok[N];
	void DDFS(int x)
	{
		ok[x]=1;
		for(int i=H[x]; i; i=nxt[i])
			if(!ok[to[i]]) DDFS(to[i]);
	}
	bool Check()
	{
		DDFS(1);
		for(int i=2; i<=n; ++i) if(!ok[i]) return 1;
		return 0;
	}
}
namespace Violence
{
	int fa[N];
	int Get_fa(int x){
		return x==fa[x]?x:fa[x]=Get_fa(fa[x]);
	}
	int Kruskal(int id)
	{
		for(int i=1; i<=n; ++i) fa[i]=i;
		int res=0;
		for(int r1,r2,k=2,i=1; i<=m; ++i)
		{
			if(e[i].fr==id||e[i].to==id) continue;
			if((r1=Get_fa(e[i].fr))==(r2=Get_fa(e[i].to))) continue;
			fa[r1]=r2, res=std::max(res,e[i].val);
			if(++k==n) return std::max(mn[id],res);
		}
		return -1;
	}
	void Main()
	{
		LL res=0;
		for(int t,i=1; i<=n; ++i) res+=Kruskal(i);
		printf("%lld\n",res);
	}
}
int Get_fa(int x){
	return x==ff[x]?x:ff[x]=Get_fa(ff[x]);
}
bool cmp_e(int a,int b){
	return e[a].val<e[b].val;
}
inline bool Check(int x,int s)
{
	for(int i=0,lim=bel[x].size(); i<lim; ++i) if(bel[x][i]==s) return 1;
	return 0;
}
int Kruskal(int now,int id)//某连通块内MST 
{
	Vec &s=scc[now]; int tot=s.size(),res=0,m=0;
	for(int i=1; i<=n; ++i) ff[i]=i;//
	for(int i=0,x; i<tot; ++i)
	{
		ff[x=s[i]]=x;
		for(int j=0,lim=edge[x].size(); j<lim; ++j)
			tmp[++m]=edge[x][j];
	}
	std::sort(tmp+1,tmp+1+m,cmp_e);
	for(int k=1,j=1,r1,r2; j<=m; ++j)
	{
		int i=tmp[j];
		if(e[i].fr==id||e[i].to==id||!Check(e[i].fr,now)||!Check(e[i].to,now)) continue;
		if((r1=Get_fa(e[i].fr))==(r2=Get_fa(e[i].to))) continue;
		fa[r1]=r2, res=std::max(res,e[i].val);
		if(++k==tot) return res;
	}
	return -1;
}
void Tarjan(int x)
{
	low[x]=dfn[x]=++Index, sk[++top]=x;
	for(int v,i=H[x]; i; i=nxt[i])
		if(!dfn[v=to[i]])
		{
			fa[v]=x, Tarjan(v), low[x]=std::min(low[x],low[v]);
			if(dfn[x]==low[v])
			{
				scc[++scnt].push_back(x), bel[x].push_back(scnt);
				do{
					scc[scnt].push_back(sk[top]), bel[sk[top--]].push_back(scnt);
				}while(sk[top+1]!=v);
				val[scnt]=Kruskal(scnt,0);
				printf("scnt:%d %d\n",scnt,val[scnt]);
			}
			else if(dfn[x]<low[v]) --top/*!*/;
		}
		else if(v!=fa[x]) low[x]=std::min(low[x],dfn[v]);
}
int Calc(int now)
{
	static int vis[N],Time=0;
	++Time; int res=0;
	for(int i=0,tmp,lim=bel[now].size(),x; i<lim; ++i)
	{
		x=bel[now][i];
		if((tmp=Kruskal(x,now))==-1) return -1;
		res=std::max(res,tmp), vis[x]=Time;
	}
	for(int i=1; i<=scnt; ++i) if(vis[i]!=Time) res=std::max(res,val[i]);
	printf("Calc(%d):%d\n",now,res);
	return std::max(res,mn[now]);
}

int main()
{
//	freopen(".in","r",stdin);

	n=read(), m=read(); memset(mn,0x3f,sizeof mn);
	for(int i=1; i<=m; ++i) AddEdge(read(),read(),read());
	if(NoSolution::Check()) return printf("%d\n",-n),0;

	std::sort(e+1,e+1+m);
//	if(1ll*n*m<=1e8) return Violence::Main(),0;
	for(int i=1; i<=m; ++i) edge[e[i].fr].push_back(i), edge[e[i].to].push_back(i);
	Tarjan(1);
	for(int i=1; i<=scnt; ++i)
		for(int j=0; j<scc[i].size(); ++j) printf("scc:%d:%d\n",i,scc[i][j]);
	LL res=0;
	for(int i=1; i<=n; ++i) res+=Calc(i);
	printf("%lld\n",res);

	return 0;
}

T3

0pts:(迷)

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define mod (998244353)
typedef long long LL;
const int N=1e6+5;

int n,K,sz,S[12],pos[12];

int Check(LL x)
{
	static int tm[7];
	for(int i=1; i<=sz; ++i) tm[i]=0;
	for(; x; x/=10) ++tm[pos[x%10]];
	for(int i=1; i<=sz; ++i) if(tm[i]%3) return 0;
	return 1;
}
int Solve(LL K)
{
	LL res=0,lim=1;
	while(n--) lim*=10ll;
	LL bef=lim/10, start=bef/K;
	while(start*K<bef) ++start;
	for(LL i=start; i*K<lim; ++i) res+=Check(i*K);
	return res%mod;
}

int main()
{
//	freopen(".in","r",stdin);

	char s[12];
	scanf("%d%d%s",&n,&K,s+1); sz=strlen(s+1);
	for(int i=1; i<=sz; ++i) pos[S[i]=s[i]-'0']=i;
	if(n>15) return puts("0"),0;
	printf("%d\n",Solve(K));

	return 0;
}

15pts:

//数组感觉爆炸 没敢交。。然而sb了完全可以
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define mod (998244353)
typedef long long LL;
const int N=1e6+5;

int n,K,sz,S[12],pos[12];
int f[102][250][3][3][3][3][3][3];//2e8
bool vis[102][250][3][3][3][3][3][3];

int DFS(int p,int rem,int r1,int r2,int r3,int r4,int r5,int r6)
{
	if(!p) return !rem&&!r1&&!r2&&!r3&&!r4&&!r5&&!r6;
	if(vis[p][rem][r1][r2][r3][r4][r5][r6])
		return f[p][rem][r1][r2][r3][r4][r5][r6];

	long long res=0;
	for(int i=1; i<=sz; ++i)
		switch(i)
		{
			case 1: res+=DFS(p-1,(rem*10+S[1])%K,(r1+1)%3,r2,r3,r4,r5,r6); break;
			case 2: res+=DFS(p-1,(rem*10+S[2])%K,r1,(r2+1)%3,r3,r4,r5,r6); break;
			case 3: res+=DFS(p-1,(rem*10+S[3])%K,r1,r2,(r3+1)%3,r4,r5,r6); break;
			case 4: res+=DFS(p-1,(rem*10+S[4])%K,r1,r2,r3,(r4+1)%3,r5,r6); break;
			case 5: res+=DFS(p-1,(rem*10+S[5])%K,r1,r2,r3,r4,(r5+1)%3,r6); break;
			case 6: res+=DFS(p-1,(rem*10+S[6])%K,r1,r2,r3,r4,r5,(r6+1)%3); break;
		}
	for(int i=0; i<=9; ++i)
		if(!pos[i]) res+=DFS(p-1,(rem*10+i)%K,r1,r2,r3,r4,r5,r6);

//	printf("DFS(%d,%d,%d,%d,%d,%d,%d,%d):%I64d\n",p,rem,r1,r2,r3,r4,r5,r6,res%mod);
	vis[p][rem][r1][r2][r3][r4][r5][r6]=1;
	return f[p][rem][r1][r2][r3][r4][r5][r6]=res%mod;
}

int main()
{
	char s[12];
	scanf("%d%d%s",&n,&K,s+1); sz=strlen(s+1);
	for(int i=1; i<=sz; ++i) pos[S[i]=s[i]-'0']=i;
	printf("%d\n",DFS(n,0,0,0,0,0,0,0));

	return 0;
}
posted @ 2018-07-31 21:47  SovietPower  阅读(264)  评论(0编辑  收藏  举报