-
- Prob.1 玩具谜题
模拟、、
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct node{
int dir;
char name[15];
}nd[100005];
int p,n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%d %s",&nd[i].dir,nd[i].name);
for(int i=1,a,b;i<=m;i++){
scanf("%d%d",&a,&b);
b%=n;
if(a^nd[p].dir) p+=b;
else p-=b;
p=(p+n)%n;
}
printf("%s",nd[p].name);
return 0;
}
-
- Prob.2 天天爱跑步
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define MAXN 300005
using namespace std;
struct player{
int s,t,l;
}p[MAXN];
struct edge{
int to,next;
}E[MAXN*2],sE[MAXN],tE[MAXN],lE[MAXN];
int Head[MAXN],sHead[MAXN],tHead[MAXN],lHead[MAXN];
int fa[MAXN],dep[MAXN],tim[MAXN],c1[MAXN],c2[MAXN*2],ans[MAXN];
int Ent=2,sEnt=2,tEnt=2,lEnt=2;;
int n,m;
int find(int u){
if(fa[u]==u) return u;
return fa[u]=find(fa[u]);
}
void add(int u,int v,int &ent,int *head,edge *e){
e[ent]=(edge){v,head[u]};
head[u]=ent++;
}
void Tarjan(int u,int f){
fa[u]=u; dep[u]=dep[f]+1;
for(int i=sHead[u];i;i=sE[i].next){
int j=sE[i].to;
if(!fa[p[j].t]||p[j].l) continue;
int lca=find(p[j].t);
p[j].l=dep[u]+dep[p[j].t]-2*dep[lca];
add(lca,j,lEnt,lHead,lE);
}
for(int i=tHead[u];i;i=tE[i].next){
int j=tE[i].to;
if(!fa[p[j].s]||p[j].s==p[j].t) continue;
int lca=find(p[j].s);
p[j].l=dep[u]+dep[p[j].s]-2*dep[lca];
add(lca,j,lEnt,lHead,lE);
}
for(int i=Head[u];i;i=E[i].next){
int v=E[i].to;
if(v==f) continue;
Tarjan(v,u);
}
fa[u]=f;
}
void dfs(int u,int f){
int bc1=c1[dep[u]+tim[u]];
int bc2=c2[tim[u]-dep[u]+MAXN];
for(int i=sHead[u];i;i=sE[i].next){
c1[dep[u]]++;
}
for(int i=tHead[u];i;i=tE[i].next){
int j=tE[i].to;
c2[p[j].l-dep[u]+MAXN]++;
}
for(int i=Head[u];i;i=E[i].next){
int v=E[i].to;
if(v==f) continue;
dfs(v,u);
}
ans[u]+=c1[dep[u]+tim[u]]-bc1;
ans[u]+=c2[tim[u]-dep[u]+MAXN]-bc2;
for(int i=lHead[u];i;i=lE[i].next){
int j=lE[i].to;
if(dep[p[j].s]-dep[u]==tim[u]) ans[u]--;
c1[dep[p[j].s]]--;
c2[p[j].l-dep[p[j].t]+MAXN]--;
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,a,b;i<n;i++){
scanf("%d%d",&a,&b);
add(a,b,Ent,Head,E);
add(b,a,Ent,Head,E);
}
for(int i=1;i<=n;i++) scanf("%d",&tim[i]);
for(int i=1;i<=m;i++){
scanf("%d%d",&p[i].s,&p[i].t);
add(p[i].s,i,sEnt,sHead,sE);
add(p[i].t,i,tEnt,tHead,tE);
}
Tarjan(1,0);
dfs(1,0);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
Vijos上好像栈空间不够,要RE4组。把栈空间开大了一些,在本机测试NOI官网上的数据是AC了的。
-
- Prob.3 换教室
dp[i][j][0/1] 当前第i节课,已经申请了j次,当前是否申请的最小疲劳值。
定义出来以后,就比较好转移了。
之前定义错了,搞了好久。
启示:本题虽然是计算期望,但求得是最小期望。
而导致期望有大有小的原因就是我们的申请的位置不同。
即 申请的位置 这是一个决策选择,用dp处理。
而对于已经 申请了的位置,即确定了申请方案后,因为申请是否成功是有概率的,
所以再求出其对应的期望。
所以本题就是dp决策出最优申请方案,再在dp的同时求出对应的期望,用以辅助转移。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#define INF 0x3f3f3f3f
using namespace std;
double g[2005],dp[2005][2005][2],ans;
int c[2005],d[2005];
int dis[305][305];
int N,M,V,E;
void cmin(double &a,double b){
if(a>b) a=b;
}
void readin(){
memset(dis,0x3f,sizeof(dis));
scanf("%d%d%d%d",&N,&M,&V,&E);
for(int i=1;i<=N;i++) scanf("%d",&c[i]);
for(int i=1;i<=N;i++) scanf("%d",&d[i]);
for(int i=1;i<=N;i++) scanf("%lf",&g[i]);
for(int i=1,u,v,w;i<=E;i++){
scanf("%d%d%d",&u,&v,&w);
dis[u][v]=min(dis[u][v],w);
dis[v][u]=min(dis[v][u],w);
}
for(int i=1;i<=V;i++) dis[0][i]=0,dis[i][i]=0;
}
void floyd(){
for(int k=1;k<=V;k++)
for(int i=1;i<=V;i++)
for(int j=1;j<=V;j++){
if(dis[i][k]==INF||dis[j][k]==INF) continue;
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
void DP(){
for(int i=1;i<=N;i++)
for(int j=0;j<=M;j++) dp[i][j][0]=dp[i][j][1]=1e9;
dp[1][0][0]=dp[1][1][1]=0;
for(int i=2;i<=N;i++)
for(int j=0;j<=min(i,M);j++){
//不选择申请
cmin(dp[i][j][0],dp[i-1][j][0]
+dis[c[i-1]][c[i]]);//前面不申请
cmin(dp[i][j][0],dp[i-1][j][1]
+dis[c[i-1]][c[i]]*(1.0-g[i-1])+dis[d[i-1]][c[i]]*g[i-1]);//前面申请
//选择申请
if(!j) continue;
cmin(dp[i][j][1],dp[i-1][j-1][0]
+dis[c[i-1]][c[i]]*(1.0-g[i])+dis[c[i-1]][d[i]]*g[i]);//前面申请
cmin(dp[i][j][1],dp[i-1][j-1][1]
+dis[c[i-1]][c[i]]*(1.0-g[i-1])*(1.0-g[i])
+dis[c[i-1]][d[i]]*(1.0-g[i-1])*g[i]
+dis[d[i-1]][c[i]]*g[i-1]*(1.0-g[i])
+dis[d[i-1]][d[i]]*g[i-1]*g[i]);
}
ans=dp[N][0][0];
for(int i=1;i<=M;i++) cmin(ans,min(dp[N][i][0],dp[N][i][1]));
printf("%.2lf",ans);
}
int main(){
readin();
floyd();
DP();
return 0;
}
-
- Prob.4 组合数问题
注意到多组输入都是相同的k
所以跑一个2000*2000的组合数递推求法,并对k取模
最后在矩阵合法范围内的0的个数就是答案。 代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int dp[2005][2005],s[2005][2005];
int n,m,k,t;
int main(){
scanf("%d%d",&t,&k);
dp[1][1]=1;
for(int i=2;i<=2001;i++)
for(int j=1;j<=i;j++)
dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%k;
for(int i=1;i<=2001;i++)
for(int j=1;j<=2001;j++)
s[i][j]=(j<=i&&dp[i][j]==0)+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
while(t--){
scanf("%d%d",&n,&m);
printf("%d\n",s[n+1][m+1]);
}
return 0;
}
-
- Prob.5 蚯蚓
优先队列维护 50分
然后看了看网上给的正解,原来还可以这么单调啊。
因为割的比例固定,所以:
长的蚯蚓割了形成的前一段长度大于短的蚯蚓割了形成的前一段,
长的蚯蚓割了形成的后一段长度大于短的蚯蚓割了形成的后一段。
维护三个队列(手写)
第一个用来存储初始蚯蚓,按从大到小排好序。
第二个队列用来存储割断的蚯蚓的前一截。 (满足长度单调递减)
第三个队列用来存储割断的蚯蚓的后一截。 (满足长度单调递减)
对于当前取出的长度为x1的蚯蚓,把它割断成为了c1',c2',并放在对应的队列后面
x秒后,c1=c1'+x*d(增量) c2=c2'+x*d。
这时再取出的长度为x2+x*d的蚯蚓,把它割断成为了e1,e2,并放在对应的队列后面
那是否c1>e1,c2>e2呢?
考虑c1和e1的大小关系。
c1=q*x1+x*d
e1=q*(x2+x*d)
相减: c1-e1=q(x1-x2)+x*d*(1-q)
因为x1>x2,1>q,所以上式>0
所以满足队列具有单调性。
于是每次取出三个队列队首最大的那个来割。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
double p;
int q[3][10000005],head[3]={1,1,1},tail[3];
int n,m,d,u,v,t,add;
bool cmp(int a,int b){
return a>b;
}
void get(int &mv){
mv=-INF; int mp;
for(register int i=0;i<3;i++)
if(head[i]<=tail[i]&&q[i][head[i]]>mv)
mv=q[i][head[i]],mp=i;
head[mp]++;
}
void print(int val,int i,int lim){
if(i%t) return;
printf("%d",val);
if(i+t<=lim) printf(" ");
}
int main(){
freopen("earthworm.in","r",stdin);
freopen("earthworm.ans","w",stdout);
scanf("%d%d%d%d%d%d",&n,&m,&d,&u,&v,&t);
p=1.0*u/v; tail[0]=n;
for(register int i=1;i<=n;i++) scanf("%d",&q[0][i]);
sort(q[0]+1,q[0]+n+1,cmp);
for(register int i=1,mv;i<=m;i++){
get(mv); mv+=add;
print(mv,i,m);
add+=d;
int a=(int)(p*mv),b=mv-a;
q[1][++tail[1]]=a-add;
q[2][++tail[2]]=b-add;
}
printf("\n");
for(register int i=1,mv;i<=n+m;i++){
get(mv); mv+=add;
print(mv,i,m+n);
}
fclose(stdout);
return 0;
}
-
- Prob.6 愤怒的小鸟
预处理好转移数组(即打某两个猪的同时,最多可以打那些猪)
然后就是状压dp。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#define rint register int
using namespace std;
const double eps=1e-7;
struct pig{
double x,y;
}p[20];
int g[20][20],dp[1<<18];
int n,m,T,all;
int sign(double x){
if(fabs(x)<=eps) return 0;
return x>0?1:-1;
}
void cmin(int &a,int b){
if(a>b) a=b;
}
bool get(int i,int j,double &a,double &b){
static double k11,k12,k13,k21,k22,k23,k;
k11=p[i].x*p[i].x; k12=p[i].x; k13=p[i].y;
k21=p[j].x*p[j].x; k22=p[j].x; k23=p[j].y;
if(!sign(k12-k22)) return 0;
if(!sign(k13/k12-k23/k22)) return 0;
k=k22/k12; a=(k23-k13*k)/(k21-k11*k);
k=k21/k11; b=(k23-k13*k)/(k22-k12*k);
if(sign(a)>0) return 0;
return 1;
}
bool check(int i,double a,double b){
static double x,y;
x=p[i].x; y=p[i].y;
return !sign(a*x*x+b*x-y);
}
int main(){
freopen("angrybirds.in","r",stdin);
freopen("angrybirds.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m); all=(1<<n)-1;
memset(dp,0x3f,sizeof(dp));
memset(g,0,sizeof(g));
for(rint i=0;i<n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
for(rint i=0;i<n;i++)
for(rint j=0;j<n;j++) if(i!=j){
double a,b;
if(!get(i,j,a,b)) continue;
for(rint k=0;k<n;k++) if(check(k,a,b))
g[i][j]|=(1<<k);
}
dp[0]=0;
for(rint S=0;S<=all;S++)
for(rint i=0;i<n;i++){
if(S&(1<<i)) continue;
cmin(dp[S|(1<<i)],dp[S]+1);
for(rint k=1;k<=n;k++) if(i!=k)
cmin(dp[S|g[i][k]],dp[S]+1);
}
printf("%d\n",dp[all]);
}
return 0;
}