感觉最近有些不在状态,几场考试考的都不是非常好.
然后我这种大弱渣显然只能去P啦~~~
(虽然觉得ACM赛制还是要跪
不过总要做出选择吗.
POJ1832
首先递推算出fi表示第i位到第0位全部变成0的最小移动次数.
gi表示将第i位变成1,同时第i?1位到第0位都变成0的最小移动次数.
利用数学归纳法可以得到:
若有一个在第i位的1,将它变成0需要的最小步数为2i+1?1.
从而递推式就很容易写出来了.(我太懒就略过了
然后我们可以模拟变的过程,首先找到最高的不相同的位,然后对后面调用g,然后后面就变成了10000000...这样的情况,再从高到低依次模拟就行了.
显然需要高精度.
另外还有一种黑科技就是求出格雷码并转化为十进制数直接求差的绝对值即可.
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
static const int mod=1e4;
struct Hugeint{
int d[1000],l;
Hugeint():l(1){
memset(d,0,sizeof d);
}
inline void operator*=(const int&x){
int t=0;
for(int i=0;i<l;++i){
t+=d[i]*x;
d[i]=t%mod;
t/=mod;
}
if(t)
d[l++]=t;
}
inline void operator+=(const Hugeint&B){
l=l>B.l?l:B.l;
for(int i=0;i<l;++i){
d[i]+=B.d[i];
if(d[i]>=mod){
d[i]-=mod;
d[i+1]++;
}
}
if(d[l])
++l;
}
inline void output(){
printf("%d",d[l-1]);
for(int i=l-2;i>=0;--i)
printf("%04d",d[i]);
}
};
Hugeint pow[130];
int a[128],b[128];
Hugeint f[130],g[130];
int main(){
//freopen("tt.in","r",stdin);
int T,n,i,j;
pow[0].d[0]=1;
for(i=1;i<128;++i){
pow[i]=pow[i-1];
pow[i]*=2;
}
cin>>T;
for(int Tcase=1;Tcase<=T;++Tcase){
cin>>n;
for(i=n-1;i>=0;--i)
cin>>a[i];
for(i=n-1;i>=0;--i)
cin>>b[i];
f[0].d[0]=g[0].d[0]=0;
if(a[0]==0)
g[0].d[0]=1;
else
f[0].d[0]=1;
for(i=1;i<n;++i){
if(a[i]==0){
f[i]=f[i-1];
g[i]=g[i-1],g[i]+=pow[i];
}
else{
f[i]=g[i-1],f[i]+=pow[i];
g[i]=f[i-1];
}
}
Hugeint res;
int ins=-1;
for(i=n-1;i>=0;--i)
if(a[i]!=b[i]){
ins=i;
break;
}
if(ins<0)
puts("0");
else{
if(ins)
res+=g[ins-1];
res+=pow[0];
if(ins>0){
memset(a,0,sizeof a);
a[ins-1]=1;
for(i=ins-1;i>=0;--i){
if(a[i]!=b[i]){
res+=pow[i];
if(i)
a[i-1]=1;
}
}
}
res.output();
puts("");
}
}
return 0;
}
POJ1112
首先建立反图,若两个点之间有连边则证明不能在一个集合中.
这让我们联想到二分图.
于是对于每一个联通分量进行二染色,若存在一个连通分量不能二染色则无解.
注意题目让我们找出一组最接近的解.
对于每个联通分量,我们将染色的方案存下来,那么有用的仅仅是两个部分分别属于哪个集合.
我们做一次简单的dp并记录方案即可.
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define N 110
int head[N],next[N*N<<1],end[N*N<<1];
inline void addedge(int a,int b){
static int q=1;
end[q]=b;
next[q]=head[a];
head[a]=q++;
}
inline void make(int a,int b){
addedge(a,b);
addedge(b,a);
}
bool G[N][N];
int v[N];
int cnt;
vector<int>s[N][2];
bool f[N][N],g[N][N];
inline bool dfs(int x){
if(v[x]==-1){
v[x]=1;
s[cnt][1].push_back(x);
}
for(int j=head[x];j;j=next[j]){
if(v[end[j]]==-1){
v[end[j]]=1-v[x];
s[cnt][1-v[x]].push_back(end[j]);
if(!dfs(end[j]))
return 0;
}
else if(v[end[j]]==v[x])
return 0;
}
return 1;
}
inline int _abs(int x){
return x<0?-x:x;
}
bool ok[N];
inline void find(int x,int y){
if(x==0)
return;
if(g[x][y]==0){
for(int i=0;i<s[x][0].size();++i)
ok[s[x][0][i]]=1;
find(x-1,y-s[x][0].size());
}
else{
for(int i=0;i<s[x][1].size();++i)
ok[s[x][1][i]]=1;
find(x-1,y-s[x][1].size());
}
}
int main(){
int n;
scanf("%d",&n);
int i,j,x;
for(i=1;i<=n;++i){
while(scanf("%d",&x)&&x)
G[i][x]=1;
}
for(i=1;i<=n;++i)
for(j=i+1;j<=n;++j)
if(!(G[i][j]&&G[j][i]))
make(i,j);
memset(v,-1,sizeof v);
bool nosol=0;
for(i=1;i<=n;++i){
if(v[i]==-1){
++cnt;
if(!dfs(i)){
nosol=1;
break;
}
}
}
if(nosol)
puts("No solution");
else{
f[1][s[1][1].size()]=1;
g[1][s[1][1].size()]=1;
f[1][s[1][0].size()]=1;
g[1][s[1][0].size()]=0;
for(i=1;i<cnt;++i)
for(j=0;j<=n;++j)
if(f[i][j]){
f[i+1][j+s[i+1][0].size()]=1;
g[i+1][j+s[i+1][0].size()]=0;
f[i+1][j+s[i+1][1].size()]=1;
g[i+1][j+s[i+1][1].size()]=1;
}
int ans=0x3f3f3f3f,num;
for(i=0;i<=n;++i){
if(f[cnt][i]&&_abs(i-(n-i))<ans){
ans=_abs(i-(n-i));
num=i;
}
}
find(cnt,num);
vector<int>v1,v2;
for(i=1;i<=n;++i)
if(ok[i])
v1.push_back(i);
else
v2.push_back(i);
printf("%d",v1.size());
for(i=0;i<v1.size();++i)
printf(" %d",v1[i]);
puts("");
printf("%d",v2.size());
for(i=0;i<v2.size();++i)
printf(" %d",v2[i]);
}
return 0;
}
POJ2238
题目水的一比,只需要暴力枚举自己的四项属性,然后做一次dp看一看此时的获胜概率是多少就行了.
令fi,j,0,fi,j,1分别表示自己得到i分,另一个人得到j分,下一次自己/别人出手的概率.
把fi,j,0,fi,j,1缩成一个强连通分量,不难发现状态转移图是一个拓扑图.
于是只需要套用一般的方法:对于每一个连通分量先处理这个连通分量里面的答案,然后将概率转移到后续的连通分量里面的点,然后再后面的连通分量里面再处理就行了.
对于一个连通分量,里面的转移是存在环的,因此要列方程进行求解.
对于分数二元组(i,j),令p0表示fi,j,0,令p1表示fi,j,1,则有以下的方程:
p0=stay0p0+change1p1+c0
p1=stay1p1+change0p0+c1
其中c0,c1表示从上面的连通分量转移下来的贡献.
随便解个方程就行了.
现在问题的关键是源点怎么转移.
(跪大爷
我们可以在两个方程中的一个加上p0=1,这样我们惊奇的发现得到正确的答案啦!
但是这样转移下去是并不对的!
上述的正确答案是指只有这个连通分量时的正确答案.
也就是说源点本可以以更大的概率转移到下面的分量.
因此我们需要将这个连通分量里面所有的点的概率都除以原点的概率!(雾
其实我得出的结论是:只要方程是对的,随便搞搞就能得出正确的结果啦!
然后也很容易对拍:不断迭代就行啦!
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef double db;
int n,m;
int pt2[2],pt3[2],reb[2],def[2];
db ans=0;
db f[35][35][2],g[35][35][2];
db ok3[2],ok2[2],stay[2],change[2];
typedef pair<int,int> pii;
int deg[35][35];
bool vis[35][35];
static const int dx[4]={2,0,3,0};
static const int dy[4]={0,2,0,3};
pair<db,db>solve(db a1,db b1,db c1,db a2,db b2,db c2){
db x,y;
y=(c1*a2-c2*a1)/(b1*a2-a1*b2);
x=(c1-b1*y)/a1;
return make_pair(x,y);
}
inline db dp(){
int i,j,k;
for(i=0;i<2;++i){
db p=pt3[i]/(db)(pt2[i]+pt3[i]);
ok3[i]=p*0.8*pt3[i]/(db)(pt3[i]+def[1-i]);
ok2[i]=(1-p)*pt2[i]/(db)(pt2[i]+def[1-i]);
stay[i]=(1-ok3[i]-ok2[i])*0.8*reb[i]/(db)(reb[0]+reb[1]);
change[i]=1-ok3[i]-ok2[i]-stay[i];
}
queue<pii>q;
q.push(make_pair(0,0));
memset(vis,0,sizeof vis);
vis[0][0]=1;
memset(deg,0,sizeof deg);
pii tmp;
while(!q.empty()){
tmp=q.front();
q.pop();
int x=tmp.first,y=tmp.second;
if(x>=n||y>=n)
continue;
for(i=0;i<4;++i){
++deg[x+dx[i]][y+dy[i]];
if(!vis[x+dx[i]][y+dy[i]]){
vis[x+dx[i]][y+dy[i]]=1;
q.push(make_pair(x+dx[i],y+dy[i]));
}
}
}
memset(f,0,sizeof f);
memset(g,0,sizeof g);
db re=0;
q.push(make_pair(0,0));
while(!q.empty()){
tmp=q.front();
q.pop();
int x=tmp.first,y=tmp.second;
pair<db,db>get;
if(x==0&&y==0){
get=solve(2-stay[0],-change[1],1,-change[0],1-stay[1],0);
get.second/=get.first;
get.first=1;
}
else
get=solve(1-stay[0],-change[1],g[x][y][0],-change[0],1-stay[1],g[x][y][1]);
f[x][y][0]=get.first;
f[x][y][1]=get.second;
if(x>=n||y>=n)
continue;
g[x+2][y][0]+=f[x][y][0]*ok2[0];
g[x][y+2][1]+=f[x][y][1]*ok2[1];
g[x+3][y][0]+=f[x][y][0]*ok3[0];
g[x][y+3][1]+=f[x][y][1]*ok3[1];
for(i=0;i<4;++i)
if(!(--deg[x+dx[i]][y+dy[i]]))
q.push(make_pair(x+dx[i],y+dy[i]));
}
for(j=0;j<=34;++j)
for(k=0;k<=34;++k)
if((j>=n||k>=n)&&(j>k))
re+=f[j][k][0];
return re;
}
inline void dfs(int dep,int last){
if(dep==5&&last==0){
ans=max(ans,dp());
return;
}
for(int i=1;i<=10&&i<=last;++i){
if(dep==1)
pt2[0]=i;
else if(dep==2)
pt3[0]=i;
else if(dep==3)
reb[0]=i;
else
def[0]=i;
if(dep==3){
if(last-i>=1&&last-i<=10)
dfs(dep+1,last-i);
}
else
dfs(dep+1,last-i);
}
}
int main(){
while(scanf("%d%d%d%d%d%d",&n,&m,&pt2[1],&pt3[1],&reb[1],&def[1])!=EOF){
ans=0;
dfs(1,m);
printf("%.3lf\n",ans);
}
return 0;
}