• EZ 2018 05 06 NOIP2018 慈溪中学集训队互测(五)


    享受爆零的快感

    老叶本来是让初三的打的,然后我SB的去凑热闹了

    TM的T2写炸了(去你妹的优化),T1连-1的分都忘记判了,T3理所当然的不会

    光荣革命啊!

    T1

    思维图论题,CHJ dalao给出了正解但-1输成0了缅怀

    而且这题不能用读优玄学

    思路也很新奇,先跑一遍MST,判断是否有无解的情况

    然后看一下MST中与1相连的边有几条

    如果小于k那么我们把所有与1相连的边减上一个值使它们优先被选,然后跑MST

    大于k就加上去即可

    注意到这个值可以二分,因此不停做MST即可

    CODE

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=5005,M=100005;
    const double EPS=1e-5;
    struct data
    {
    	int l,r,s;
    	double add;
    }a[M];
    int n,m,k,cnt,father[N],tot;
    long long ans;
    inline bool comp(data a,data b)
    {
    	return a.s+a.add<b.s+b.add;
    }
    inline int getfather(int k)
    {
    	return father[k]==k?k:father[k]=getfather(father[k]);
    }
    inline int MST(double x)
    {
    	register int i; int res=0,tot=0;
    	for (i=1;i<=m;++i)
    	if (a[i].l==1||a[i].r==1) a[i].add=x;
    	sort(a+1,a+m+1,comp);
    	for (i=1;i<=n;++i)
    	father[i]=i; ans=0;
    	for (i=1;i<=m;++i)
    	{
    		int fx=getfather(a[i].l),fy=getfather(a[i].r);
    		if (fx!=fy)
    		{
    			if (a[i].l==1||a[i].r==1) ++res;
    			ans+=a[i].s; ++tot;
    			father[fx]=fy;
    		}
    	}
    	if (tot!=n-1) return -1;
    	return res;
    }
    int main()
    {
    	freopen("path.in","r",stdin); freopen("path.out","w",stdout);
    	register int i;
    	scanf("%d%d%d",&n,&m,&k);
    	for (i=1;i<=m;++i)
    	{
    		scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].s);
    		if (a[i].l==1||a[i].r==1) ++tot;
    	}
    	if (tot<k||MST(0)==-1) { puts("-1"); return 0; }
    	double l=-100000.0,r=100000.0;
    	while (r-l>EPS)
    	{
    		double mid=(l+r)/2.0; 
    		int t=MST(mid);
    		if (t==k) { printf("%lld",ans); break; }
    		if (t>k) l=mid; else r=mid;
    	}
    	return 0;
    }
    

    T2

    这么小的数据范围,就是套路的状压DP

    只有我一个人想到记忆化搜索暴力玄学搜索么

    好吧又是我ZZ了

    首先发现n的范围只有15,果断状压成01串表示第几个串选或不选

    而后预处理一个数组g[i][j]表示当t[i]上的字符为j时,与其它串的匹配情况(用二进制下压缩01串表示)

    例如,四个串为

    ?a?
    ab?
    aa?
    ??b

    例如

    • g[2]['a']=1101(与第四个,第三个,第一个串分别匹配)=13

    • g[1]['a']=1111=15

    然后我们设DP数组f[i][j]表示匹配到第i位时(第i位尚未匹配)所有串的匹配情况为j(二进制下的01串)时方案总数

    则有f[i][j]可以转移出

    f[i+1][j&g[i][ch]]+=f[i][j]('a'<=ch<='z')
    

    因为这里只要一个位置不匹配那就整个串都不匹配,因此只有两边都是1才满足要求

    最后ans+=所有j的1的个数恰好为k个的f[len+1][j]

    C++没有以字符为下标的数组,因此所有字符减去'a'即可

    CODE

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=20,D=50,mod=1000003;
    int g[D+5][30],f[D+5][(1<<N+1)+5],n,k,ans,t;
    char s[N][55],ch;
    inline int count(int x)
    {
    	int tot=0;
    	while (x) tot+=x&1,x>>=1;
    	return tot;
    }
    int main()
    {
    	freopen("question.in","r",stdin); freopen("question.out","w",stdout);
    	register int i,j; scanf("%d",&t);
    	while (t--)
    	{
    		memset(f,0,sizeof(f));
    		memset(g,0,sizeof(g));
    		scanf("%d%d",&n,&k);
    		for (i=1;i<=n;++i)
    		scanf("%s",s[i]+1); int len=strlen(s[1]+1);
    		for (i=1;i<=len;++i)
    		for (ch='a';ch<='z';++ch)
    		for (j=1;j<=n;++j)
    		if (s[j][i]=='?'||ch==s[j][i]) g[i][ch-'a']|=1<<j-1;
    		f[1][(1<<n)-1]=1; ans=0;
    		for (i=1;i<=len;++i)
    		for (j=0;j<1<<n;++j)
    		if (f[i][j]) for (ch='a';ch<='z';++ch)
    		f[i+1][j&g[i][ch-'a']]=(f[i+1][j&g[i][ch-'a']]+f[i][j])%mod;
    		for (i=0;i<1<<n;++i)
    		if (count(i)==k) ans=(ans+f[len+1][i])%mod;
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    T3

    这就是传说中的神题么

    暴力的想法都是有的,但是就是刚不出来(只有YZC dalao拿了2分)

    标算是DP+KMP+矩阵乘法(快速幂),还是不可食用的

    想要标算的同学看BZOJ1009黄学长题解

  • 相关阅读:
    【C++类与对象】实验四(二)
    【微信小程序——开发步骤1】
    【C++/实验三】类和对象
    【C++/类与对象总结】
    【C++/函数】实验2
    食堂APP-项目开发 语音输入(1)
    食堂APP-项目开发及踩坑记录(7)
    食堂APP-项目开发及踩坑记录(6)
    食堂APP-项目开发及踩坑记录(5)
    食堂APP-项目开发及踩坑记录(4)
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9007644.html
Copyright © 2020-2023  润新知