比赛链接:
http://202.197.224.59/OnlineJudge2/index.php/Contest/problems/contest_id/31
^_^ 中文题目就不说题目意思啦。
Problem A Number (XTU 1192)
题目链接:
http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1192
解题思路:
贪心。
如果n是偶数,直接除以2,如果是奇数,看+1还是-1所得数的2的因子个数多。3另外单独处理(是减一,不是加一)。
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
ll n;
int Ti(ll cur) //求出cur中因子2的个数
{
int cnt=0;
while(cur%2==0&&cur)
{
cnt++;
cur/=2;
}
return cnt;
}
int main()
{
while(~scanf("%I64d",&n))
{
int ans=0;
while(n)
{
ans++;
if(n%2==0) //是偶数直接除以2
{
n/=2;
continue;
}
if(n==3||n==1)
{
n--;
continue;
}
int a=Ti(n+1),b=Ti(n-1);
if(a>b) //如果是+1 得到2的次数多
n++;
else //如果次数相等或小于 选择小的
n--;
}
printf("%d\n",ans);
}
return 0;
}
Problem B Lukey Wheel (XTU 1193)
题目链接:
http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1193
解题思路:
概率dp.求期望+预处理。
至上限x=1000,y=1000. 从后往前考虑,dp[i][j]:表示已经获得i个一等奖,j个二等奖,到达获得1000个一等奖1000个二等奖还需转动次数的期望。
显然当i=1000时,dp[i][j]=1/8*(1+dp[i][j+1])+7/8*(1+dp[i][j]) 也即:dp[i][j]=1/8*dp[i][j+1]+8 一等奖已经是上限了,再获得一等奖无贡献(即本身dp[i][j])。
同理当j=1000时,dp[i][j]=1/8*(1+dp[i+1][j])+7/8*(1+dp[i+1][j]) 也即:dp[i][j]=1/8*dp[i+1][j]+8 二等奖次数已达到上限,再获得二等奖无贡献(即本身dp[i][j])。
当i<1000&&j<1000时 dp[i][j]=1/8*(1+dp[i+1][j])+1/8*(1+dp[i][j+1]+6/8(1+dp[i][j]) 当前这次的转动要么是一等奖,要么是二等奖,要么都不是(即本身dp[i][j])。
输入一个x,y,直接输出dp[1000-x][1000-y]即可。
代码:
//#include<CSpreadSheet.h>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define Maxn 1100
double dp[Maxn][Maxn];
int main()
{
int x,y;
x=1000;
y=1000;
dp[x][y]=0;
for(int j=y-1;j>=0;j--)
dp[x][j]=8+dp[x][j+1];
for(int j=x-1;j>=0;j--)
dp[j][y]=8+dp[j+1][y];
for(int i=x-1;i>=0;i--)
{
for(int j=y-1;j>=0;j--)
dp[i][j]=1.0/2.0*(1+dp[i+1][j])+1.0/2.0*(1+dp[i][j+1])+3;
}
while(~scanf("%d%d",&x,&y))
printf("%.6lf\n",dp[1000-x][1000-y]);
return 0;
}
题目链接:
http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1194
解题思路:
裸的扩展欧几里得算法。
显然题目要求a*x+b*y=n,正整数<x,y>的对数。
分享一篇写的比较好的扩展欧基里德算法文章:http://chhaj5236.blog.163.com/blog/static/112881081200942542255916/
设k=gcd(a,b)
显然如果n不能被k整除,无解。方程可化简为a*x/(n/k)+b*y/(n/k)=gcd(a,b)=gcd(b,a%b)=b*x2+(a-a/b*b)*y2
x=n/k*x1 y=n/k*y1 x1=y2 y1=x2-a/b*y2
a,b,n同时除以gcd(a,b).
求出最小的正整数解x,a(x+p*b)+b*(y-p*a)=n. 只要满足a(x+p*b)<=n即可,也即p<=(n/a-x)/b 详细请见代码。
代码:
//#include<CSpreadSheet.h>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
ll ex_gcd(ll a,ll b,ll &x,ll &y) //扩展欧几里德算法
{
if(!b)
{
x=1;
y=0;
return a;
}
ll g=ex_gcd(b,a%b,x,y);
ll temp=x;
x=y;
y=temp-a/b*y;
return g;
}
int main()
{
ll a,b,n;
while(~scanf("%I64d%I64d%I64d",&a,&b,&n))
{
if(a==b)
{
if(n%a==0)
printf("1\n");
else
printf("0\n");
continue;
}
ll x,y,bb;
ll gg=ex_gcd(a,b,x,y);
if(n%gg) //无解
{
printf("0\n");
continue;
}
x=x*n/gg; //求出一个解
y=y*n/gg;
b=b/gg;
a=a/gg;
n/=gg;
x=x%b;
if(x<0) //求出x的最小的正整数解
x+=b;
ll temp=(n-x*a)/b; //求出y
//printf("::x:%I64d y:%I64d\n",x,temp);
if(temp<0) //此时y如果小于0的话,肯定不存在都是正整数的解
{
printf("0\n");
continue;
}
printf("%d\n",(n/a-x)/b+1); //推出求个数的式子
}
return 0;
}
Problem D Hexa Kill
题目链接:
http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1195
解题思路:
拓扑排序+状压dp
先拓扑排序判断依赖关系是否有环。
dp[i]表示在走了状态为i的节点后,需要的最小的能量能量消化。预处理出每个节点的前驱(压缩成一个整数)。
dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+en[j][num[i]+1];
num[i]表示状态为i时,节点个数。
^-^-^ 用二维矩阵保存就有问题,用vector就没问题,不知道什么原因,我怀疑这个oj的数据有问题。
代码:
//#include<CSpreadSheet.h>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define Maxn 21
int n,m;
int in[Maxn],dp[1<<Maxn],en[Maxn][Maxn];
int num[1<<Maxn];
int be[Maxn];
queue<int>myq;
vector<int>myv[Maxn];
//bool hav[Maxn][Maxn];
bool Tp() //拓扑排序判断是否有环
{
while(!myq.empty())
myq.pop();
for(int i=1;i<=n;i++)
if(!in[i])
myq.push(i);
int cnt=0;
while(!myq.empty())
{
int temp=myq.front();
myq.pop();
cnt++;
/* for(int i=1;i<=n;i++)
{
if(!in[i]||!hav[temp][i])
continue;
in[i]--;
if(!in[i])
myq.push(i);
}*/
for(int i=0;i<myv[temp].size();i++)
{
if(!(--in[myv[temp][i]]))
myq.push(myv[temp][i]);
}
}
if(cnt==n)
return true;
return false;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
//printf("%d\n",(1<<18)*18);
//system("pause");
while(~scanf("%d%d",&n,&m))
{
memset(in,0,sizeof(in));
memset(be,0,sizeof(be));
memset(num,0,sizeof(num));
//memset(hav,false,sizeof(hav));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) //en[i][j]表示第i中材料第j步放需要的能量
{
scanf("%d",&en[i][j]);
//hav[i][j]=false;
}
myv[i].clear();
}
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
in[x]++;
be[x]|=(1<<(y-1)); //第x中材料的前驱材料状态
//hav[y][x]=true;
myv[y].push_back(x);
}
if(!Tp()) //有环
{
printf("Enemy Hexa Kill\n");
continue;
}
memset(dp,INF,sizeof(dp));
dp[0]=0;
for(int i=0;i<(1<<n);i++) //
{
for(int j=1;j<=n;j++) //
{
if(dp[i]==INF) //当前状态不满足
continue;
if((1<<(j-1))&i) //没有走到
continue;
if((be[j]&i)==be[j]) //前驱状态满足
{
if(dp[i]+en[j][num[i]+1]<dp[i|(1<<(j-1))])
{
dp[i|(1<<(j-1))]=dp[i]+en[j][num[i]+1];
num[i|(1<<(j-1))]=num[i]+1; //个数
}
}
}
}
printf("%d\n",dp[(1<<n)-1]);
}
return 0;
}
/*
5 5
2 3 2 1 1
1 2 1 3 3
2 1 3 3 3
1 2 1 2 1
1 1 2 1 1
2 3
2 4
5 4
1 2
1 5
*/
Problem E Tree
题目链接:
http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1196
解题思路:
DFS+线段树(区间更新、单点查询)
对于节点u,增加d,它对子树节点v的影响是,(dep[v]-dep[u]+1)*d 化简得d*dep[v]+(1-dep[u])*d 显然对于每个点v,dep[v]是确定的,只用维护一个一次函数的两个系数即可。
每次更新时,只用更新子树节点区间的这个两个系数。
一遍DFS编号,确定每个节点所维护的区间范围,线段树更新。
=_= =_= 不知道为什么用vector就RE,用邻接表就可以过,求大神指正。
代码:
//#include<CSpreadSheet.h>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define Maxn 110000
vector<int>myv[Maxn];
int to[Maxn],n,dep[Maxn],dd,q,from[Maxn];
ll ta,tb;
int cnt;
struct Node //维护两个系数
{
ll a,b;
}node[Maxn<<2];
struct Edge //邻接表建树
{
int v;
struct Edge * next;
}edge[Maxn],*head[Maxn];
void add(int a,int b) //添加边
{
++cnt;
edge[cnt].v=b;
edge[cnt].next=head[a];
head[a]=&edge[cnt];
}
void build(int l,int r,int rt)
{
node[rt].a=node[rt].b=0;
if(l==r)
return ;
int m=(l+r)>>1;
build(lson);
build(rson);
}
void pushdown(int rt)
{
if(node[rt].a)
{
node[rt<<1].a+=node[rt].a;
node[rt<<1|1].a+=node[rt].a;
node[rt].a=0;
}
if(node[rt].b)
{
node[rt<<1].b+=node[rt].b;
node[rt<<1|1].b+=node[rt].b;
node[rt].b=0;
}
}
int dfs(int cur,int hi) //返回子树的最大编号 ,求出每个节点维护的区间
{
++dd;
from[cur]=to[cur]=dd; //编号
dep[cur]=hi; //深度
struct Edge * p=head[cur];
//for(int i=0;i<myv[cur].size();i++)
while(p)
{
int temp=dfs(p->v,hi+1);
to[cur]=max(temp,to[cur]);
p=p->next;
}
return to[cur];
}
void query(int x,int l,int r,int rt) //单点查询
{
if(l==r)
{
ta=node[rt].a; //返回最终的两个系数
tb=node[rt].b;
return ;
}
pushdown(rt);
int m=(l+r)>>1;
if(x<=m)
query(x,lson);
else
query(x,rson);
}
void update(int L,int R,ll va,ll vb,int l,int r,int rt) //更新区间的系数值
{
if(L<=l&&R>=r)
{
node[rt].a+=va;
node[rt].b+=vb;
return ;
}
int m=(l+r)>>1;
pushdown(rt);
if(L<=m)
update(L,R,va,vb,lson);
if(R>m)
update(L,R,va,vb,rson);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
/* for(int i=0;i<=n;i++)
myv[i].clear();*/
int tt;
cnt=0;
memset(head,NULL,sizeof(head));
for(int i=1;i<=n;i++)
{
int temp;
scanf("%d",&temp);
//myv[temp].push_back(i);
add(temp,i);
if(temp==0)
tt=i;
}
dd=0;
dfs(tt,1);
build(1,n,1);
scanf("%d",&q);
while(q--)
{
ll ord,x,d;
scanf("%I64d",&ord);
if(ord==2)
{
scanf("%I64d",&x);
//x=from[x];
//printf("x:%I64d %d\n",x,dep[x]);
query(from[x],1,n,1); //单点查询
printf("%I64d\n",ta*dep[x]+tb);
}
else
{
scanf("%I64d%I64d",&x,&d);
//printf("x:%I64d %d\n",x,dep[x]);
update(from[x],to[x],d,d*(1-dep[x]),1,n,1); //区间更新
}
}
}
return 0;
}