T1 网格
显然 n==m时,卡特兰数
n,m<=100时,滚动数组的dp
正解
ans=C(n+m,n)-C(n+m,n+1)
不会证,但是可以类比 cantelan数公式 C(2*n,n)-C(2*n,n-1)
#pragma GCC optimize("O2")
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
const int N=5006;
const int LEN=20006;
struct bign
{
int s[LEN],len;
bign(){mem(s,0);len=1;}
void clear(){mem(s,0);len=1;}
bign operator * (const int c)
{
bign t=*this;
int jin=0;
for(int i=1;i<=t.len;++i)
{
t.s[i]=(t.s[i]*c+jin);
jin=t.s[i]/10;
t.s[i]%=10;
}
t.s[t.len+1]=jin;
++t.len;
while(t.s[t.len]==0&&t.len>1)
--t.len;
while(t.s[t.len]>=10)
{
t.s[t.len+1]=t.s[t.len]/10;
t.s[t.len]%=10;
++t.len;
}
return t;
}
bign operator / (const int c)
{
bign t=*this;
for(int i=t.len;i>=1;--i)
{
t.s[i-1]+=t.s[i]%c*10;
t.s[i]/=c;
}
while(t.s[t.len]==0&&t.len>1)
--t.len;
return t;
}
bign operator + (bign &c)
{
bign now=*this;
//printf("yuan
");
//now.out1();
//c.out1();
now.len=max(c.len,now.len)+1;
for(int i=1;i<=now.len;++i)
{
now.s[i+1]+=(now.s[i]+c.s[i])/10;
now.s[i]=(now.s[i]+c.s[i])%10;
}
while(now.s[now.len]==0&&now.len>1)
--now.len;
//now.out1();
return now;
}
bign operator - (bign &c)
{
bign now=*this;
for(int i=1;i<=now.len;++i)
{
if(now.s[i]<c.s[i])
{
--now.s[i+1];
now.s[i]+=10;
}
now.s[i]-=c.s[i];
}
while(now.s[now.len]==0&&now.len>1)
--now.len;
return now;
}
void out1()
{
for(int i=len;i>=1;--i)
printf("%d",s[i]);
//printf("
");
}
};
int n,m;
bign a,b,c;
// C(n+m,n)-C(n+m,n+1)
int main(){
scanf("%d%d",&n,&m);
a.s[1]=1;b.s[1]=1;
for(int i=1;i<=n;i+=2)
a=a*( (n+m-i+1) * (i+1>n?1:n+m-i) );
for(int i=1;i<=n;i+=2)
a=a/(i*(i+1>n?1:i+1));
for(int i=1;i<=n+1;i+=2)
b=b*( (n+m-i+1) * (i+1>n+1?1:n+m-i) );
for(int i=1;i<=n+1;i+=2)
b=b/(i*(i+1>n+1?1:i+1));
//a.out1();
//b.out1();
c=a-b;
c.out1();
}
T2
欧拉通路:从图中一个点出发不重复地遍历所有边的路径(可以停在另一个点)
欧拉回路:从图中一个点出发不重复地遍历所有边的回路(必须回到出发点)
欧拉图:存在欧拉回路的图。判断无向图为欧拉图的充要条件是所有点的度数均为偶数。
半欧拉图:存在欧拉通路的图。判断无向图为欧拉图的充要条件是所有点的度数均为偶数或只有两个点的度数
为奇数。
一个图中如果存在度数为奇数的点,那么这样的点一定有偶数个。
(证明:deg和(边数*2)是偶数,所以显然了)
把两个操作分开考虑,先熔断,在连接
1.对于只有一个联通快
(1)是环 ans=0
(2)是欧拉图 ans=(deg大于2的点)
(3)否则 (deg大于2的点)+(deg奇数点/2)
2.对于多个
(1)是环,直接熔断ans+1,liansum+1
(2)是欧拉图,在熔断deg>2点同时熔断成链,ans+(deg>2_sum),lainsum+1
(3)否则 ans+(deg>2_sum) liansum+(奇数点/2)
finaans=ans+liansum
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=500006;
struct son
{
int v,next;
}a1[N*2];
int first[N*2],e;
void addbian(int u,int v)
{
a1[e].v=v;
a1[e].next=first[u];
first[u]=e++;
}
int n,m;
int deg[N];
int l0[N],r0[N];
bool vis[N];
int tot,cc;
void dfs1(int x)
{
vis[x]=1;
++cc;
int temp;
for(int i=first[x];i!=-1;i=a1[i].next)
{
temp=a1[i].v;
if(vis[temp])
continue;
dfs1(temp);
}
}
int work1()// 鍙�湁涓€涓�仈閫氬潡
{
//printf("work1
");
int cntji=0,cntou=0,cntda=0;
for(int i=1;i<=n;++i)
{
if(deg[i]&1)
++cntji;
else
++cntou;
if(deg[i]>2)
++cntda;
}
if(cntji==0)// 鏄��鎷夊洖璺� OR 鐜�
return cntda;
return cntda+cntji/2;// 鍏堢啍鏂�垚閾撅紝鍦ㄦ帴璧锋潵
}
int sum,cntji,cntou,cntda;
void dfs(int x)
{
vis[x]=1;
++sum;
if(deg[x]&1)
++cntji;
else
++cntou;
if(deg[x]>2)
++cntda;
int temp;
for(int i=first[x];i!=-1;i=a1[i].next)
{
temp=a1[i].v;
if(vis[temp])
continue;
dfs(temp);
}
}
int work2()
{
printf("work2
");
mem(vis,0);
int liansum=0,ans=0;
for(int i=1;i<=m;++i)
{
if( vis[l0[i]]||vis[r0[i]] )
continue;
sum=cntji=cntou=cntda=0;
dfs( (l0[i]?l0[i]:r0[i]) );
if(cntji==0)
{
if(!cntda)
cntda=1;//鏄�竴涓�幆
ans+=cntda;
++liansum;
}
else
{
ans+=cntda;
liansum+=cntji/2;
}
}
printf("ans=%d liansum=%d
",ans,liansum);
return ans+liansum;
}
// 鑷�幆涓嶇敤鑰冭檻锛屽洜涓鸿仈閫氬揩閲屽彧鏈変竴涓�嚜鐜�椂锛屾寜鐓х幆绠�
// 涓嶅彧鏈変竴涓�嚜鐜�椂锛宒eg[杩欎釜鐐筣>2锛岀啍鏂�殑鏃跺€欓『渚跨啍鏂�簡
int main(){
//freopen("frame4.in","r",stdin);
mem(first,-1);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&l0[i],&r0[i]);
if(!l0[i])
l0[i]=++n;
if(!r0[i])
r0[i]=++n;// 鍔犱笂鏂扮偣
++deg[l0[i]];
++deg[r0[i]];
addbian(l0[i],r0[i]);
addbian(r0[i],l0[i]);
}
cc=0;tot=0;
for(int i=1;i<=n;++i)
if(deg[i])
++tot;
for(int i=1;i<=n;++i)
if(deg[i])
{
dfs1(i);
break;
}
if(tot==cc)
cout<<work1();
else
cout<<work2();
}
T3
我打的不是正解,但是A了,理论复杂度过不了...
f[i][j][k] 第i个平民 j从它到根节点的状态 选了k个平民打仗
转移的时候就求出i和i-1的LCA,然后用一个t[][]数组处理出来i和i-1公共部分 各状态 选择平民打仗数量 的最优解,然后转移即可
正解是 树规 先暴搜出所有状态,再用类似dp的东西统计ans
#pragma GCC optimize("O2")
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
const int N=1106;
ll inf;
int n,m;
int maxp,num,p[14];
ll w[N][N];
int hou[N][14],zhan[N][14];
//int lca[N][N];
ll f[2][N][N];
int now;
void chu()
{
for(int i=1;i<=num;++i)
for(int j=0;j<=maxp;++j)
for(int k=1;k<n;++k)
{
if( (j&(1<<k)) && (j&1) )
w[i][j]+=zhan[i][k];
else
if( !(j&(1<<k)) && !(j&1) )
w[i][j]+=hou[i][k];
}
/*int temp,temp1,temp2;
for(int i=1;i<=num;++i)
for(int j=i+1;j<=num;++j)
{
temp=0;temp1=((1<<(n-1))-1)+i;temp2=((1<<(n-1))-1)+j;
while(temp1!=temp2)
{
++temp;
temp1/=2;
temp2/=2;
}
lca[i][j]=temp;
}*/
}
ll t[N][N];
void out11()
{
printf("
");
for(int i=0;i<=maxp;++i)
{
for(int j=1;j<=m;++j)
printf("%lld ",t[i][j]);
printf("
");
}
printf("
");
}
int LCA(int x,int y)
{
int t1=((1<<(n-1))-1)+x,t2=((1<<(n-1))-1)+y;
int cc=0;
while(t1!=t2)
{
++cc;
t1>>=1;
t2>>=1;
}
return cc;
}
void work()
{
mem(f,-50);inf=-f[0][0][0];
for(int i=0;i<=maxp;++i)
f[0][i][i&1]=w[1][i];
int temp,temp1,nowp;
int hhh;
ll tt=0;
now=0;
for(int i=2;i<=num;++i)
{
now^=1;
hhh=(i>m?m:i);
temp=LCA(i-1,i);temp1=n-temp;
for(int j=0;j<=p[temp1];++j)
for(int nu=0;nu<=hhh;++nu)
f[now][j][nu]=-inf;
for(int j=0;j<=p[temp1];++j)
{
nowp=(j<<temp);
for(int nu=0;nu<=hhh;++nu)
{
t[nowp][nu]=-inf;
for(int k=0;k<=p[temp];++k)
if(t[nowp][nu]<f[now^1][nowp|k][nu])
t[nowp][nu]=f[now^1][nowp|k][nu];
//t[nowp][nu]=max(t[nowp][nu],f[now^1][nowp|k][nu]);
}
}
//out11();
for(int j=0;j<=p[temp1];++j)
{
nowp=j<<temp;
for(int nu=0;nu<=hhh;++nu)
{
for(int k=0;k<=p[temp];++k)
{
tt=t[nowp][nu]+w[i][nowp|k];
if(f[now][nowp|k][nu+(k&1)]<tt)
f[now][nowp|k][nu+(k&1)]=tt;
//f[now][nowp|k][nu+(k&1)]=max(f[now][nowp|k][nu+(k&1)],t[nowp][nu]+w[i][nowp|k]);
}
}
}
}
ll ans=0;
for(int i=0;i<=maxp;++i)
for(int j=0;j<=m;++j)
if(ans<f[now][i][j])
ans=f[now][i][j];
//ans=max(ans,f[now][i][j]);
cout<<ans;
}
//0_houqin 1_zuozhan
int main(){
//freopen("T3.in","r",stdin);
for(int i=1;i<=12;++i)
p[i]=(1<<i)-1;
scanf("%d%d",&n,&m);
maxp=(1<<n)-1;num=(1<<(n-1));
for(int i=1;i<=num;++i)
for(int j=1;j<n;++j)
scanf("%d",&zhan[i][j]);
for(int i=1;i<=num;++i)
for(int j=1;j<n;++j)
scanf("%d",&hou[i][j]);
chu();
work();
}