比赛链接:
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;
}
题目链接:
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; }