思路:
非常简单的一个模拟
代码:
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+100;
int a[N],n,m,cr[N];
char s[N][50];
int main() {
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i) {
scanf("%d",&cr[i]);
cin>>s[i];
}
int ans=0;
for(int i=1;i<=m;++i) {
int x,y;
scanf("%d%d",&x,&y);
if(x==cr[ans])
ans=(ans-y%n+n)%n;
else
ans=(ans+y%n)%n;
}
cout<<s[ans];
return 0;
}
思路:
并不是很难得一个期望dp(虽然一开始没做出来233),首先跑一边floyd找出最短路,用f[i][j][0/1]表示前i短时间,换了j次,第i段时间换(1)还是不换(0)的最小耗费。
如果不换,那么当前最有花费就是第i-1短时间换(就用前一个换的情况加上换之后的教室到当前教室的距离,当然还要考虑期望,有k[i-1]的概率会换成功,有(1-k[i-1])的概率不成功)或者不换(就直接用前一个不换的情况加上前一个教室到当前教室的距离)中更优秀的那个。即
f[i][j][0]=min(f[i-1][j][0]+a[c[i-1]][c[i]],f[i-1][j][1]+a[d[i-1]][c[i]]*k[i-1]+a[c[i-1]][c[i]]*(1-k[i-1]))
如果换,那么就比较麻烦一点,但是只要慢慢想出所有情况就可以了。
首先如果前一个不换,那么就有k[i]的概率是从c[i-1] (不换情况下的教室)到d[i] (换之后的教室)。有(1-k[i])的概率是从c[i-1]到c[i]。所以
f[i][j][1]=min(f[i][j][1],f[i-1][j-1][0]+(a[c[i-1]][d[i]])*k[i]+(a[c[i-1]][c[i]])*(1-k[i]))
如果前一个也换的换的话,那么有k[i]k[i-1]的概率是从d[i-1]到d[i],有k[i](1-k[i-1])的概率是从c[i-1]到d[i],有(1-k[i])k[i-1]的概率是从d[i-1]到c[i],有(1-k[i-1])(1-k[i-1])的概率是从c[i-1]到c[i],所以
f[i][j][1]=min(f[i][j][1], f[i-1][j-1][1]+(a[d[i-1]][d[i]])*k[i]*k[i-1]+a[d[i-1]][c[i]]*(1-k[i])*k[i-1]+a[c[i-1]][d[i]]*k[i]*(1-k[i-1])+a[c[i-1]][c[i]]*(1-k[i])*(1-k[i-1]))
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=310,M=2000+10;
const double INF=999999999;
int n,m,V,E;
int c[M],d[M];
double a[M][M];
double K[M],f[M][M][2];
inline void init() {
cin>>n>>m>>V>>E;
for(int i=1;i<=V;++i) for(int j=1;j<=V;++j) a[i][j]=INF;
for(int i=1;i<=n;++i)
cin>>c[i];
for(int i=1;i<=n;++i)
cin>>d[i];
for(int i=1;i<=n;++i)
cin>>K[i];
for(int i=1;i<=E;++i) {
int x,y;
cin>>x>>y;
double z;
cin>>z;
a[x][y]=a[y][x]=min(a[x][y],z);
}
}
inline void floyd() {
for(int i=1;i<=V;++i) a[i][i]=0;
for(int k=1;k<=V;++k)
for(int i=1;i<=V;++i)
for(int j=1;j<=V;++j)
if(a[i][j]>a[i][k]+a[k][j])
a[i][j]=a[i][k]+a[k][j];
}
int main() {
ios::sync_with_stdio(false);
init();
floyd();
if(m==0) {
double mans=0;
for(int i=2;i<=n;++i)
mans+=a[c[i-1]][c[i]];
printf("%.2f\n",mans);
return 0;
}
/*for(int i=1;i<=V;++i) {
for(int j=1;j<=V;++j) {
printf("%.0f ",a[i][j]);
}
printf("\n");
}*/
for(int i=1;i<=n;++i)
for(int j=0;j<=m;++j)
f[i][j][1]=f[i][j][0]=INF;
f[1][1][1]=0;
f[1][0][0]=0;
for(int i=2;i<=n;++i) {
for(int j=0;j<=min(m,i);++j) {
f[i][j][0]=min(f[i-1][j][0]+a[c[i-1]][c[i]],f[i-1][j][1]+a[d[i-1]][c[i]]*K[i-1]+a[c[i-1]][c[i]]*(1-K[i-1]));
if(j!=0)
f[i][j][1]=min(f[i-1][j-1][0]+a[c[i-1]][d[i]]*K[i]+a[c[i-1]][c[i]]*(1-K[i]),f[i-1][j-1][1]+a[d[i-1]][d[i]]*K[i]*K[i-1]+a[d[i-1]][c[i]]*(1-K[i])*K[i-1]+a[c[i-1]][d[i]]*(1-K[i-1])*K[i]+a[c[i-1]][c[i]]*(1-K[i])*(1-K[i-1]));
}
}
double ans=INF;
for(int j=0;j<=m;++j) {
ans=min(ans,f[n][j][0]);
ans=min(ans,f[n][j][1]);
}
printf("%.2f",ans );
return 0;
}
思路:
对于上面和下面的式子进行分解质因数,然后看看上面的质因数个数减去下面的质因数个数能不能达到k的质因数的要求即可。
分解质因数的时候用对于阶乘分解质因数的常用方法:比如要求1999!中能分解出多少个5,那么就把1999不断的除以5,并且把得到的数相加即可。原因显然。
但是上面方法的复杂度是nnt,明显tle,考虑优化。发现当k固定之后,对于每个n和m是固定的,并且似乎是可以转移的。所以考虑预处理。
用c[i][j]表示\(C_i^j\)是不是符合要求。用g[i][j]表示当m为j,n从j到max满足条件的数量。f[i][j]表示n为1到i,m为1到j时满足条件的数量。
然后只要考虑出f[i][j]的转移即可,显然f[i][j]=f[i][j-1]+g[i][j]
然后只要O(1)查询即可,懒得现将询问读入再预处理,所以前面的预处理全都是到2000的。
虽然跑的很慢,但是能过啊!!
80分思路:
一个比较明显的思路就是用优先队列模拟。每次取出队首也就是最长的那个蚯蚓,然后将它割开,重新放回队列,然后考虑其他蚯蚓增长的问题,如果挨个加上的话肯定t飞掉了,所以可以用一个变量now表示当前将每个蚯蚓都加上了st,每取出一只蚯蚓都将它的长度加上now即可,注意割开之后的蚯蚓不能加上这次增长的长度,要处理一下。然后就有80分了
100分思路:
可以发现,因为是先对最长的蚯蚓进行处理,并且割开的比例相同,所以先割开的蚯蚓割开后的占大比例的那一块肯定比后割开的要长,同理,占小比例的那块可能也比后割开的要长,这样就有了单调性,只要用三个队列,分别表示原来的蚯蚓,割开后占大比例的蚯蚓,和割开后占小比例的蚯蚓。每次操作的时候从这三个队列的队首中找出最大的那条蚯蚓进行与80分做法类似的操作即可,操作之后再分别放入第2,3个队列。(虽然是100分做法,但是一不小心就会写挂,所以还是写80分或者分段程序保险!!)
80分代码:
#include<queue>
#include<cstdio>
#include<iostream>
#define p u/v
using namespace std;
typedef long long ll;
priority_queue<ll>q;
double sta;
ll now,n,m,u,v,sum,T;
int main() {
cin>>n>>m>>sum>>u>>v>>T;
for(int i=1;i<=n;++i) {
int x;
cin>>x;
q.push(x);
}
for(int i=1;i<=m;++i) {
ll k=q.top()+now;
q.pop();
if(i%T==0) {
printf("%lld ",k);
}
ll z=k*p;
q.push(z-now-sum);
q.push(k-z-now-sum);
now+=sum;
}
printf("\n");
int js=0;
while(!q.empty()) {
js++;
if(js%T==0) printf("%lld ",q.top()+now);
q.pop();
}
return 0;
}
100分代码:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<queue>
#include<algorithm>
using namespace std;
#define p u/v
typedef long long ll;
queue<ll>q[4];
ll read() {
ll x=0;ll f=1;char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
bool cmp(int x,int y) {
return x>y;
}
ll a[7100000+100],now,sum;
int main() {
ll n=read(),m=read(),sum=read(),u=read(),v=read(),t=read();
//cout<<p<<endl;
q[0].push(-0x7fffffff);
for(int i=1;i<=n;++i)
a[i]=read();
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;++i)
q[1].push(a[i]);
/*for(int i=1;i<=n;++i)
cout<<a[i]<<" ";
while(!q[1].empty()) {
cout<<q[1].front()<<" ";
q[1].pop();
}*/
for(int i=1;i<=m;++i) {
int k=0;
for(int j=1;j<=3;++j)
if(!q[j].empty()&&q[j].front()>q[k].front()) k=j;
ll z=q[k].front();
q[k].pop();
z+=now;
if(i%t==0) printf("%lld ",z);
ll zz=z*p;
//cout<<z<<" "<<zz<<endl;
q[2].push(zz-now-sum);
q[3].push(z-zz-now-sum);
now+=sum;
}
printf("\n");
for(int i=1;i<=n+m;++i) {
int k=0;
for(int j=1;j<=3;++j)
if(!q[j].empty()&&q[j].front()>q[k].front()) k=j;
if(i%t==0) printf("%lld ",q[k].front()+now);
q[k].pop();
}
return 0;
}
思路:
搜索+最优化剪枝
(不多bb是因为抄的题解啊啊啊啊)
代码:
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const double eps=1e-8;
bool dy(double a,double b) {
return fabs(a-b)<eps;
}
int n,m,ans;
double x[20],y[20],pwxa[20],pwxb[20],tx[20],ty[20];
void dfs(int c,int u,int v) {
if(u+v>=ans) return;
if(c>n) {
ans=u+v;
return;
}
bool flag=0;
for(int i=1;i<=u;++i) {
if(dy(pwxa[i]*x[c]*x[c]+pwxb[i]*x[c],y[c])) {
dfs(c+1,u,v);
flag=1;
break;
}
}
if(!flag) {
for(int i=1;i<=v;++i) {
if(dy(x[c],tx[i])) continue;
double a=(y[c]*tx[i]-ty[i]*x[c])/(x[c]*x[c]*tx[i]-tx[i]*tx[i]*x[c]);
double b=(y[c]-x[c]*x[c]*a)/x[c];
if(a<0) {
pwxa[u+1]=a;
pwxb[u+1]=b;
double q=tx[i],w=ty[i];
for(int j=i;j<v;++j) {
tx[j]=tx[j+1];
ty[j]=ty[j+1];
}
dfs(c+1,u+1,v-1);
for(int j=v;j>i;--j) {
tx[j]=tx[j-1];
ty[j]=ty[j-1];
}
tx[i]=q;
ty[i]=w;
}
}
tx[v+1]=x[c];
ty[v+1]=y[c];
dfs(c+1,u,v+1);
}
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%lf%lf",&x[i],&y[i]);
ans=100;
dfs(1,0,0);
printf("%d\n",ans);
}
return 0;
}
总结
难度就不说了,为啥没有天天爱跑步,不会。。。。但是打满暴力就可以拿一等奖。虽然天天爱跑步的恶心程度难以想象,但是部分分还是可以拿的,即便很不幸,天天爱跑步只拿10分,又很不幸期望dp写挂了只拿5分,day1还可以有100+10+5=115。又很不幸组合数只拿了质数的那40分,又又很不幸蚯蚓只会那80分的优先队列,又又又很不幸愤怒的小鸟一分也没有,day2也有40+80+0=120分了,总共就有了235分了,这刚好是山东分数线啊。而且这么不幸的话可以去跳楼了