• SDOI2019 R2D2 题解


    A 热闹的聚会与尴尬的聚会

    第一问是个奇怪的东西,第二问求最大独立集,题目要求两问的答案都要尽量大。

    如果你觉得这个题是纯乱搞,两问都不可做,那应该就没什么分数了。

    考虑第一问的非 NP-hard 做法:贪心,删除度数最小的点,更新当前局面的答案,显然是一个先变大后减小的过程,可以用堆模拟。

    第二问无脑随机即可,能过。

    #include<stdio.h>
    #include<vector>
    #include<queue>
    #include<algorithm>
    using namespace std;
    
    int T,n,m;
    vector<int> G[10005];
    
    struct node {
    	int a,b;
    	node(int x=0,int y=0):a(x),b(y){}
    };
    
    bool operator < (node s,node t)
    {
    	return s.b>t.b;
    }
    
    int main()
    {
    	scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d%d",&n,&m);
    		for(int i=1; i<=n; i++)
    			G[i]=vector<int>();
    		for(int i=1,u,v; i<=m; i++)
    		{
    			scanf("%d%d",&u,&v);
    			G[u].push_back(v);
    			G[v].push_back(u);
    		}
    		vector<int> del,deg(n+1),vis(n+1);
    		priority_queue<node> q;
    		for(int i=1; i<=n; i++)
    			q.push(node(i,deg[i]=G[i].size()));
    		int ans=-1,num=0;
    		while(!q.empty())
    		{
    			node t=q.top(); q.pop();
    			if(vis[t.a])
    				continue;
    			if(t.b>ans)
    				ans=t.b,num=del.size();
    			del.push_back(t.a);
    			vis[t.a]=1;
    			for(auto &j:G[t.a])
    				if(!vis[j])
    				{
    					--deg[j];
    					q.push(node(j,deg[j]));
    				}
    		}
    		printf("%d",(int)del.size()-num);
    		for(int j=num; j<(int)del.size(); j++)
    			printf(" %d",del[j]);
    		puts("");
    		vector<int>ord(n);
    		for(int i=0; i<n; i++)ord[i]=i+1;
    		vector<int>get;
    		ans=-1;
    		random_shuffle(ord.begin(),ord.end());
    		vis=vector<int>(n+1);
    		del=vector<int>();
    		for(auto x:ord)
    			if(!vis[x])
    			{
    				vis[x]=1;
    				del.push_back(x);
    				for(auto &j:G[x])vis[j]=1;
    			}
    		if((int)del.size()>ans)
    		{
    			ans=del.size();
    			get=del;
    		}
    		random_shuffle(ord.begin(),ord.end());
    		vis=vector<int>(n+1);
    		del=vector<int>();
    		for(auto x:ord)
    			if(!vis[x])
    			{
    				vis[x]=1;
    				del.push_back(x);
    				for(auto &j:G[x])vis[j]=1;
    			}
    		if((int)del.size()>ans)
    		{
    			ans=del.size();
    			get=del;
    		}
    		random_shuffle(ord.begin(),ord.end());
    		vis=vector<int>(n+1);
    		del=vector<int>();
    		for(auto x:ord)
    			if(!vis[x])
    			{
    				vis[x]=1;
    				del.push_back(x);
    				for(auto &j:G[x])vis[j]=1;
    			}
    		if((int)del.size()>ans)
    		{
    			ans=del.size();
    			get=del;
    		}
    		random_shuffle(ord.begin(),ord.end());
    		vis=vector<int>(n+1);
    		del=vector<int>();
    		for(auto x:ord)
    			if(!vis[x])
    			{
    				vis[x]=1;
    				del.push_back(x);
    				for(auto &j:G[x])vis[j]=1;
    			}
    		if((int)del.size()>ans)
    		{
    			ans=del.size();
    			get=del;
    		}
    		printf("%d",ans);
    		for(auto x:get)
    			printf(" %d",x);
    		puts("");
    	}
    	return 0;
    }
    

    B 移动金币

    我们记第 (i) 堆石子的数目为从右手数第 (i) 个棋子到第 (i+1) 个棋子(或者右边界)的距离。
    这就是一个阶梯 Nim 模型。
    阶梯 Nim 的 SG 函数等于奇数位置的石子的 SG 值的异或,别的位置没有意义,
    那么可以考虑 dp 出 (f_k) 表示 (k) 个石子放在奇数位置且异或和 (=0) 的方案数,
    然后用组合数把剩下来的石子插入到中间即可。
    可以按二进制位逐个背包,复杂度容易写到 (O(nklog n)) 或更低。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    typedef long long ll;
    const int mod=1000000009,SIZE=1<<18;
    inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
    inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
    inline int mul(int a,int b){static ll r;r=(ll)a*b;return r>=mod?r%mod:r;}
    inline int power(int a,int b,int res=1){
    	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
    	return res;
    }
    inline void Inc(int &a,int b){(a+=b)>=mod&&(a-=mod);}
    inline void Dec(int &a,int b){(a-=b)<0&&(a+=mod);}
    inline void Mul(int &a,int b){a=mul(a,b);}
    
    int n,m,ans,C[55][55],fac[SIZE],ifac[SIZE],inv[SIZE],f[SIZE];
    
    int choose(int n,int m){
    	return mul(fac[n],mul(ifac[n-m],ifac[m]));
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=0;i<=m;++i)
    		for(int j=C[i][0]=1;j<=i;++j)
    			C[i][j]=add(C[i-1][j-1],C[i-1][j]);
    	inv[0]=inv[1]=ifac[0]=ifac[1]=fac[0]=fac[1]=1;
    	for(int i=2;i<SIZE;++i){
    		fac[i]=mul(fac[i-1],i);
    		inv[i]=mul(inv[mod%i],mod-mod/i);
    		ifac[i]=mul(ifac[i-1],inv[i]);
    	}
    	int c=(m+1)/2,r=m+1-c;
    	int d=n-m;
    	f[0]=1;
    	for(int k=1;k<=d;k<<=1)
    		for(int i=d;i>=k<<1;--i)
    			for(int j=2;i>=j*k&&j<=c;j+=2)
    				Inc(f[i],mul(f[i-j*k],C[c][j]));
    	for(int s=0;s<=d;++s)Inc(ans,mul(f[s],choose(d-s+r-1,r-1)));
    	printf("%d",dec(choose(n,m),ans));
    	return 0;
    }
    

    C 连续子序列

    0
    01
    0110
    01101001
    0110100110010110

    随便划出一个区间作映射 (<01 ightarrow 0,10 ightarrow 1>) 都能递归到上一层,长度缩一半,映射关系是双向且唯一的。
    (f(str,k)) 表示串 (str) 跟着 (k) 个字符能匹配的方案数然后就每次长度缩一半了。

    可以证明当 (|S|+k>3) 时有唯一切割方案,也就是 (dfs) 两边下去有至少一个是 (0),所以不用管算重。
    (|S|+kle 3) 的情况特判边界 return 即可。

    #include<map>
    #include<iostream>
    #include<string>
    #include<algorithm>
    #define SZ(x) ((int)(x).size())
    using namespace std;
    
    typedef long long ll;
    const int P=1000000009;
    
    map<pair<string,ll>,int> mem;
    
    int dfs(const string&s,ll k)
    {
    	if(SZ(s)==1)
    	{
    		if(k==0)
    			return 1;
    		if(k==1)
    			return 2;
    		if(k==2)
    			return 3; //!!!
    	}
    	if(SZ(s)==2)
    	{
    		if(k==0)
    			return 1;
    		if(k==1)
    			return s[0]==s[1]?1:2;
    	}
    	if(SZ(s)==3&&!k)
    		return !(s[0]==s[1]&&s[1]==s[2]); //!!!
    	auto now=make_pair(s,k);
    	if(mem.count(now))
    		return mem[now];
    	string d;
    	int ans=0,ok=1;
    	for(int i=0; i<SZ(s); i+=2)
    	{
    		if(s[i]==s[i+1])
    		{
    			ok=0;
    			break;
    		}
    		d+=s[i];
    	}
    	if(ok)
    		ans=(ans+dfs(d,(k-SZ(s)%2+1)/2))%P;
    	ok=1;
    	d=s[0]^1;
    	for(int i=1; i<SZ(s); i+=2)
    	{
    		if(s[i]==s[i+1])
    		{
    			ok=0;
    			break;
    		}
    		d+=s[i];
    	}
    	if(ok)
    		ans=(ans+dfs(d,(k-(SZ(s)-1)%2+1)/2))%P;
    	mem[now]=ans;
    	return ans;
    }
    
    int main()
    {
    	int t; string s; ll k;
    	for(cin>>t;t;--t)
    		cin>>s>>k,cout<<dfs(s,k)<<endl;
    	return 0;
    }
    
  • 相关阅读:
    获取本机外网ip和内网ip
    服务器发布MVC常见问题解决方案
    Ext.Net学习笔记01:在ASP.NET WebForm中使用Ext.Net
    Form验证(转)
    各浏览器各版本User-agent汇总 欢迎补充
    MSSQL中把表中的数据导出成Insert
    发布mvc3的项目时system.web.mvc 版本 为3.0.0.1高于服务器版本3.0.0.0 升级到3.0.0.1
    MySQL Packets larger than max_allowed_packet are not allowed
    SQL查看数据库所用用户表数量和使用的空间
    公用提示对话框
  • 原文地址:https://www.cnblogs.com/bestwyj/p/13023222.html
Copyright © 2020-2023  润新知