zoukankan      html  css  js  c++  java
  • 6.28 NOI模拟赛 好题 状压dp 随机化

    avatar
    avatar

    算是一道比较新颖的题目 尽管好像是两年前的省选模拟赛题目。。

    对于20%的分数 可以进行爆搜,对于另外20%的数据 因为k很小所以考虑上状压dp.

    观察最后答案是一个连通块 从而可以发现这个连通块必然存在一个深度最浅的点且唯一 所以随便找一个点做根然后对自己子树内寻找答案就可以是正确的。

    考虑另外的30%的数据k<=3 可是颜色数最多可以有n个 不知道哪个是最终答案。

    一次状压dp的复杂度:(2^{2k}cdot n)

    容易得到可以暴力枚举一下 然后要做 (C(n,3)) 这样会TLE。

    此时可以考虑一种随机化:每次随机三个颜色是谁 这样一直跑...

    显然这个和上面那个差不多 也是一个低效率的随机 这里要用到一个非常好用的思路 曾经在[GXOI/GZOI2019]旅行者 这道题中有类似的体现。

    后者是 发现每次跑最短路可以跑多源对多源的且一些不优的点对对答案无影响 才可以进一步的使用不断的分组来得到答案。

    而这个 可以考虑另外一种比较高效的随机 不需要每次都求出到底是哪几个点是答案 而我们只需要让其在答案中得到体现。

    做法是 由于只有k个位置 给每个颜色rand一个位置然后直接求 这样做可以发现如果不是最优解 那么显然答案会更小 对答案没影响。

    观察一下这样随机的效率(frac{k!}{k^k}) 每做一次是0.03的概率 那么做多次概率就>1了 在期望的角度是很容易得到正确答案的。

    这里我加了卡时 强制跑0.8s再退出 这样正确性会更高.

    code
    //#include<bits/stdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<ctime>
    #include<cstdlib>
    #include<cctype>
    #include<cstring>
    #include<cmath>
    #include<string>
    #include<utility>
    #include<queue>
    #include<vector>
    #include<algorithm>
    #include<deque>
    #include<stack>
    #include<list>
    #include<bitset>
    #include<set>
    #include<map>
    #define INF 1000000000000000000ll
    #define rep(p,n,i) for(int i=p;i<=n;++i)
    #define fep(n,p,i) for(int i=n;i>=p;--i)
    #define vep(p,n,i) for(int i=p;i<n;++i)
    #define db double
    #define get(x) x=read()
    #define put(x) printf("%d
    ",x)
    #define pb push_back
    #define ll long long
    #define db double
    #define putl(x) printf("%lld
    ",x)
    using namespace std;
    char *fs,*ft,buf[1<<15];
    inline char getc()
    {
    	return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
    	int x=0,f=1;char ch=getc();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    	return x*f;
    }
    const int MAXN=10010,N=5;
    int n,k,len,ans,maxx;
    int a[MAXN],b[MAXN],c[MAXN],fa[MAXN];
    int f[MAXN][1<<N];
    int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1];
    inline void add(int x,int y)
    {
    	ver[++len]=y;nex[len]=lin[x];lin[x]=len;
    	ver[++len]=x;nex[len]=lin[y];lin[y]=len;
    }
    inline void dp(int x,int v)
    {
    	fa[x]=v;
    	for(int i=lin[x];i;i=nex[i])
    	{
    		int tn=ver[i];
    		if(tn==v)continue;
    		dp(tn,x);
    	}
    }
    inline void dfs(int x)
    {
    	f[x][1<<c[x]-1]=1;
    	for(int i=lin[x];i;i=nex[i])
    	{
    		int tn=ver[i];
    		if(tn!=fa[x])
    		{
    			dfs(tn);
    			fep(maxx,1,w1)fep(maxx,1,w2)f[x][w1|w2]=min(f[x][w1|w2],f[x][w1]+f[tn][w2]);
    		}
    	}
    	ans=min(ans,f[x][maxx]);
    }
    int main()
    {
    	freopen("hao.in","r",stdin);
    	freopen("hao.out","w",stdout);
    	double st=clock();
    	get(n);get(k);int mx=0;
    	rep(1,n,i)get(a[i]),mx=max(mx,a[i]);
    	rep(2,n,i)add(read(),read());
    	dp(1,0);maxx=1<<k;--maxx;
    	srand(time(0));ans=n;
    	double ed=clock();
    	while(ed-st<=880)
    	{
    		memset(f,0x3f,sizeof(f));
    		rep(1,mx,j)b[j]=rand()%k+1;
    		rep(1,n,j)c[j]=b[a[j]];
    		dfs(1);
    		ed=clock();
    	}
    	put(ans);return 0;
    }
    
  • 相关阅读:
    暑假集训Day14 I (莫队)
    暑假集训Day14 G (gcd 数学)
    暑假集训Day13 H (进制转换 数学)
    暑假集训Day13 C (二分+贪心)
    暑假集训Day13 I (区间DP)
    暑假集训Day12 H (数论 STL)
    Java8中list转map方法总结
    Typora ---一款简洁的Markdown编辑器
    java8 map flatmap
    Elasticsearch基本查询总结
  • 原文地址:https://www.cnblogs.com/chdy/p/13204937.html
Copyright © 2011-2022 走看看