noip模拟赛#39

昨晚打开的题想了一会发现都不会后决定慢慢想。而后早上开校会的时候莫名其妙的都想出来了。。。git

T1:m=100,ai=50000,i<=5。1到m的数每一个数只能用一次,判断是否可以有这些数的某些数相乘获得ai算法

=>啊O(mai)的dp?利用质因数分解来写?不会。不会。直到早上以为数据范围那么小写个爆搜+剪枝应该能过吧,毕竟是T1。并且50000的话每一个数的因数都挺少的。app

正解:优化

预处理出剩下前k张卡片时对于每一个质因数最多能够贡献多少次幂。若搜索到一个状态,就算剩下的卡片全部均可以使用且不考虑分配原则的状况下仍没法约去全部质因数,则一定无解,能够进行剪枝操做。再配合算法一,指望得分100%。spa

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
	int x=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return x;
} 
const int nmax=1e3+5;
const int inf=0x7f7f7f7f;
int a[10][nmax],t[10],n,m;
bool vis[nmax];
bool dfs(int x,int pre,int now){
	if(x>n) return 1;
	int td=t[x]/now;
	if(td<=a[x][pre]) return 0;
	if(td<=m&&!vis[td]) {
		vis[td]=1;
		if(dfs(x+1,0,1)) return 1;
		vis[td]=0;
	}
	rep(i,pre+1,a[x][0]){
		if(td%a[x][i]==0&&!vis[a[x][i]]) {
			vis[a[x][i]]=1;
			if(dfs(x,i,now*a[x][i])) return 1;
			vis[a[x][i]]=0;
		}
	}
	return 0;
}
int main(){
	freopen("cards.in","r",stdin);freopen("cards.out","w",stdout);
	int T=read();
	rep(_T,1,T){
		clr(vis,0);
		n=read(),m=read();
		rep(i,1,n) t[i]=read();
		rep(i,1,n) a[i][0]=0;
		rep(i,1,n) rep(j,2,m) if(t[i]%j==0) a[i][++a[i][0]]=j;
		if(!dfs(1,0,1)) puts("Yes");else puts("No");
	}
	fclose(stdin);fclose(stdout);
	return 0;
}
/*
4
2 3
2 3
2 3
3 6
2 5
4 6
2 7
24 30
*/

T2:给出一个n*m的矩阵。矩阵中的数表示高度。给出初始水的高度和出水口。求出每一个点最后的高度。blog

=>嗯矩阵这铁定应该是乱搞+bfs。。而后我就想着乱搞啊乱搞啊。。死定了。。而后在一个地方卡住了,有可能有不少条路径能够走啊这bfs的顺序根本没办法保证啊这。。。而后忽然发现这不是最短路吗?个人思惟能力仍是太弱了。。。可是终于想出来了happy。spfa新写法被坑了很久。。get

=>裸的spfa只能70分。正解要加个SLF优化。。。不会写。。。string

正解:it

对于某个点,设T表示流完水后,此点的高度(若是上面没水,就是它自己的高度,不然就是水的高度)。同时,T又能够表示此点到源点的路径上,通过的最小的T的值,毫无疑问,咱们就是须要让每一个点的T最小。而且,若是某点的T表示的是水的高度,他就能够被他周围的,比他小的T更新。io

最后每一个点的答案就是他的T减他自己的高度。指望得分70%。

SPFA加入经典的SLF优化,就能够经过100%的数据了。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<queue>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
	int x=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return x;
}
const int nmax=805;
const int inf=0x7f7f7f7f;
int dist[nmax][nmax],a[nmax][nmax],mp[nmax][nmax];
int xx[4]={0,0,1,-1};
int yy[4]={1,-1,0,0};
int q[16777216][2];bool inq[nmax][nmax];
int main(){
	freopen("shower.in","r",stdin);freopen("shower.out","w",stdout);
	int n=read(),m=read(),h=read();
	rep(i,1,n) rep(j,1,m) a[i][j]=read();
	int sa=read(),sb=read();
	rep(i,1,n) rep(j,1,m) mp[i][j]=max(a[i][j]-a[sa][sb],0);
	if(h<a[sa][sb]) {
		rep(i,1,n){
			rep(j,1,m) printf("%d ",max(h-a[i][j],0));printf("\n");
		}
		return 0;
	}
	clr(dist,0x7f);dist[sa][sb]=0;
	int ql=0,qr=1,x,y,tx,ty,td;q[qr][0]=sa;q[qr][1]=sb;
	while(ql!=qr){ //这里不能是ql<qr!!!!!!! 
		ql=((ql+1)&1048575);x=q[ql][0];y=q[ql][1];inq[x][y]=0;
		rep(i,0,3){
			tx=x+xx[i];ty=y+yy[i];
			if(tx<1||ty<1||tx>n||ty>m) continue;
			td=max(dist[x][y],mp[tx][ty]);
			if(dist[tx][ty]>td){
				dist[tx][ty]=td;
				if(!inq[tx][ty]){
					inq[tx][ty]=1;qr=((qr+1)&1048575);
					q[qr][0]=tx;q[qr][1]=ty;
				}
			}
		}
	}
	rep(i,1,n){
		rep(j,1,m) printf("%d ",dist[i][j]+a[sa][sb]-a[i][j]);printf("\n");
	}
	fclose(stdin);fclose(stdout);
	return 0;
} 
/*
3 3 66
6 9 1
7 8 1
6 8 1
3 2
*/

T3:给出两颗树,在两棵树的任两个点连边能够造成一棵树。求n*m种树的最长路径。

=>很明显应该先求出原树中每一个点的最长路径。这个树形dp一下能够解决。而后接下来就是有多是原树中的路径或是通过新加入的边的路径。那么sort一下而后二分找出那么临界点就能够了。复杂度O(nlogn)。ps:其实nm1e5根本不用sort好不TAT。。。。

正解:

         明显地,若是咱们肯定了i点(不妨设i在N个点的树上),要在另外一棵树上找到j点使得新生成的最长链是直径,则只须要知足f(j)>len-1-f(i),如此可使用桶维护s(x)表示有多少f(j)是小于等于x的,那么肯定了i后,f(i)会对答案贡献M-s(len-1-f(i))次,自己两棵树上的较长直径会对答案贡献s(len-1-f(i))次。

         对于快速地求出全部的f(i)咱们能够选择树形DP后旋根。

         在树形DP内咱们能够记f(i)为i到以i节点为根的子树下任意节点的最长链。G(i)为与f(i)不相交的次长链。转移的时候只须要把i的全部儿子的f值+1中最大的赋值给f(i),次大的(不须要严格比f(i)小)赋值给g(i)。

         而后咱们就知道了根节点到树上任意一点的最长链。树上的每一个点中MAX{g(i)+f(i)}便是直径。

         而后咱们须要作的是旋根操做。

         若是咱们已经知道了根i的f值,而且须要把根转给儿子j。则须要把j对i的f值影响删去再把i看成j的儿子,把i的f值+1与j的f值g值比较。而后就知道了j做为根时的f值与g值(变换后i的g值多是错误的,可是不影响旋根,因此不须要特意维护)。

就这样遍历整颗树一遍,每一个点在其自己的树上能够延伸的最长链f(i)的长度就能够算出来了。

         时间复杂度为O(N+M)指望得分100%。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define qwq(x) for(edge *o=head[x];o;o=o->next)
#define ll long long
int read(){
	int x=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return x;
} 
const int nmax=2e5+5;
const int inf=0x7f7f7f7f;
int f[nmax],g[nmax];ll sm[nmax];
struct edge{
	int to;edge *next;
};edge es[nmax<<1],*pt=es,*head[nmax];
void add(int u,int v){
	pt->to=v;pt->next=head[u];head[u]=pt++;
	pt->to=u;pt->next=head[v];head[v]=pt++;
}
void dfs(int x,int fa){
	qwq(x) if(o->to!=fa){
		dfs(o->to,x);
		f[x]=max(f[x],f[o->to]+1);
	}
}
void DFS(int x,int fa){
	//printf("(%d %d)\n",x,fa);
	int ta=-1,tb=-1,ca=0;
	qwq(x) if(o->to!=fa) {
		if(f[o->to]>ta) tb=ta,ta=f[o->to],ca=o->to;
		else if(f[o->to]>tb) tb=f[o->to];
	}
	if(ta!=-1&&tb!=-1) ta+=2,tb+=2;
	else if(ta<0&&tb<0) ta=0;
	else tb=0;
	qwq(x) if(o->to!=fa){
		if(o->to!=ca) g[o->to]=max(g[x]+1,ta);
		else g[o->to]=max(g[x]+1,tb);
	}
	qwq(x) if(o->to!=fa) DFS(o->to,x);
}
int find(int x,int r){
	int l=0,ans=0,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(f[mid]<=x) ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}
//二分的边界什么的老是写错。我开始是弄成g[0]=inf。而后f[mid]>=x 判断。。。而后Wa了三个点。要注意一下。 
int main(){
	freopen("connect.in","r",stdin);freopen("connect.out","w",stdout);
	int n=read(),m=read(),u,v;
	rep(i,1,n-1) u=read(),v=read(),add(u,v);
	rep(i,1,m-1) u=read()+n,v=read()+n,add(u,v);
	dfs(1,0);dfs(n+1,0);DFS(1,0);DFS(n+1,0);
	
	rep(i,1,n+m) g[i]=max(g[i],f[i]);
	rep(i,1,m) f[i]=g[i+n];
	sort(g+1,g+n+1);sort(f+1,f+m+1);
	rep(i,1,m) sm[i]=sm[i-1]+f[i];
	
	int mx=max(g[n],f[m]),cur;ll ans=0;
	rep(i,1,n) {
		cur=find(mx-g[i]-1,m);
		ans+=1ll*cur*mx+1ll*(m-cur)*(g[i]+1)+sm[m]-sm[cur];
	}
	printf("%lld\n",ans);
	fclose(stdin);fclose(stdout);
	return 0;
}
/*
3 5
1 2
1 3
1 2
1 3
3 4
4 5
4+8+14=26

*/
相关文章
相关标签/搜索