2014-2015 HNOI集训

题目基本都不会……写点什么,以后好复习。

Day3:

图论部分:

1、[POI2011] Party

这题真是乱搞啊……

首先如果两个点之间没有边,那么肯定不能被一起选在答案里。

不妨设一个答案集合S,如果两个点之间没有边,且这两点还都在S中,就同时删去这两点。

那么因为不在团内的节点只有n个,而若两点间没有边,则必有一个点是不在2n个点的团内的,所以这样的删除操作只有n次。

于是就构造出了一个n个点的团……

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
int n,m,x,y,a[3010][3010],ans[3010],tot;
bool used[3010];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),a[x][y]=a[y][x]=1;
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			if(a[i][j]==0&&!used[i]&&!used[j])
				used[i]=used[j]=true;
	for(int i=1;i<=n;i++) if(!used[i]) ans[++tot]=i;
	for(int i=1;i<n/3;i++) printf("%d ",ans[i]);
	printf("%d\n",ans[n/3]);
	return 0;
}

2、CF472D

因为树上的边权均为正值,所以树上两点间的最短路一定大于任意一条连接树上两点路径上的边。

那么如果把最短路权值看做一个图,考虑Kruskal的过程,原树上所有边一定在这个图上出现过,而在Kruskal过程中,我们不会加入原树上不存在的边,因为由上一条性质,在加入这条边之前,真正在树上的边已经加完了。

然后再判定一下是否合法就可以了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
struct Edge{int f,t,w;}E[4000010];
int n,a[2010][2010],tot,fa[2010],d[2010],e;
int head[10010],to[10010],w[10010],next[10010],cnt;
inline void adde(int f,int t,int ww)
{
	cnt++,to[cnt]=t,next[cnt]=head[f],head[f]=cnt,w[cnt]=ww;
	cnt++,to[cnt]=f,next[cnt]=head[t],head[t]=cnt,w[cnt]=ww;
}
bool cmp(const Edge &i,const Edge &j){return i.w<j.w;}
int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
void DFS(int x,int fa)
{
	for(int i=head[x];i;i=next[i])
		if(to[i]!=fa) d[to[i]]=d[x]+w[i],DFS(to[i],x);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			E[++tot].f=i,E[tot].t=j,E[tot].w=a[i][j];
	sort(E+1,E+tot+1,cmp);
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=tot;i++)
		if(E[i].w>0&&find(E[i].f)!=find(E[i].t))
			e++,adde(E[i].f,E[i].t,E[i].w),fa[find(E[i].f)]=find(E[i].t);
	if(e!=n-1) {puts("NO");return 0;}
	for(int i=1;i<=n;i++)
	{
		d[i]=0;
		DFS(i,0);
		for(int j=1;j<=n;j++) if(d[j]!=a[i][j]) {puts("NO");return 0;}
	}
	puts("YES");
	return 0;
}
Day 4:

CF453D

这个题实现起来有点恶心……

首先我们可以把题目抽象成,给你两个向量A,B,让你求C = A * B ^ t,这里乘法运算是指异或卷积。

那么可以很轻松地利用FWT+快速幂解决……

但是题目给的mod可以是任何数,而FWT的utf有一个除以2的过程。

解决方法是:在FWT过程中不除以这个2,而在最后整体除以2^m。

但是我们还是要取mod啊!最后还是不能除以2^m啊!我们只能把p扩大2^m倍了,这样就可以除以2^m。

但是p扩大以后就是long long了!于是就要用long long乘long long模long long,但是用快速乘法就会TLE……

只好利用一个O(1)的取模方法T_T

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
const int MAXN=2000010;
int n,m,d[MAXN];
LL t,p,a[MAXN],B[MAXN],b[MAXN],k[MAXN];
LL mul(LL a,LL b)
{
    return (a*b-(LL)((long double)a*b/p)*p)%p;
}
void tf(LL a[],int l,int r)
{
    if(l+1==r) return;
    int mid=(l+r)>>1,len=mid-l;
    tf(a,l,mid),tf(a,mid,r);
    for(int i=l;i<mid;i++)
    {
        LL x1=a[i],x2=a[i+len];
        a[i]=(x1-x2+p)%p;
        a[i+len]=(x1+x2)%p;
    }
}
void utf(LL a[],int l,int r)
{
    if(l+1==r) return;
    int mid=(l+r)>>1,len=mid-l;
    for(int i=l;i<mid;i++)
    {
        LL x1=a[i],x2=a[i+len];
        a[i]=(x1+x2)%p;
        a[i+len]=(x2-x1+p)%p;
    }
    utf(a,l,mid),utf(a,mid,r);
}
LL qpow(LL x,LL n)
{
    LL pp=x,con=1;
    while(n>0)
    {
        if(n&1) con=mul(con,pp);
        pp=mul(pp,pp);
        n>>=1;
    }
    return con;
}
int main()
{
    scanf("%d%I64d%I64d",&m,&t,&p);
    n=1<<m,p*=n;
    for(int i=0;i<n;i++) scanf("%I64d",&a[i]),a[i]%=p;
    for(int i=0;i<=m;i++) scanf("%I64d",&B[i]),B[i]%=p;
    for(int i=1;i<n;i++) d[i]=d[i>>1]+(i&1);
    for(int i=0;i<n;i++) b[i]=B[d[i]];
    tf(a,0,n),tf(b,0,n);
    for(int i=0;i<n;i++) b[i]=qpow(b[i],t);
    for(int i=0;i<n;i++) a[i]=mul(a[i],b[i]);
    utf(a,0,n);
    for(int i=0;i<n;i++) printf("%I64d\n",a[i]>>m);
    return 0;
}


你可能感兴趣的:(2014-2015 HNOI集训)