第一问是个奇怪的东西,第二问求最大独立集,题目要求两问的答案都要尽量大。
如果你觉得这个题是纯乱搞,两问都不可做,那应该就没什么分数了。
考虑第一问的非 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;
}
我们记第 \(i\) 堆石子的数目为从右手数第 \(i\) 个棋子到第 \(i+1\) 个棋子(或者右边界)的距离。
这就是一个阶梯 Nim 模型。
阶梯 Nim 的 SG 函数等于奇数位置的石子的 SG 值的异或,别的位置没有意义,
那么可以考虑 dp 出 \(f_k\) 表示 \(k\) 个石子放在奇数位置且异或和 \(=0\) 的方案数,
然后用组合数把剩下来的石子插入到中间即可。
可以按二进制位逐个背包,复杂度容易写到 \(O(nk\log 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;
}
0
01
0110
01101001
0110100110010110
随便划出一个区间作映射 \(<01\rightarrow 0,10\rightarrow 1>\) 都能递归到上一层,长度缩一半,映射关系是双向且唯一的。
\(f(str,k)\) 表示串 \(str\) 跟着 \(k\) 个字符能匹配的方案数然后就每次长度缩一半了。
可以证明当 \(|S|+k>3\) 时有唯一切割方案,也就是 \(dfs\) 两边下去有至少一个是 \(0\),所以不用管算重。
\(|S|+k\le 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;
}
原文:https://www.cnblogs.com/bestwyj/p/13023222.html