总结:
符合noip的风格,比较考验思维;
注意细节
T1:
众所周知, wyh 是一名高二党,正把自己投入到学奥赛这一热火
朝天的工作中。
在一个天高云淡、风和日丽的下午,你和神犇 wyh 又缓缓向着
机房走去。 正当你准备上楼梯时, wyh 突发奇想, 提出了一个问
题: 如果他一次能迈一阶台阶乃至多阶,那么他走到四楼一共有
多少种可能的方案。 wyh 比较懒, 因此他还想知道他最少需要抬多
少次腿(无论一次走几阶楼梯都算抬一次腿)。 因为某校比较穷,
完工仓促, 所以修建的每个台阶之间的间距并不完全相同。其中
有些台阶比较高, 有时一个台阶的高度甚至能比上多个台阶。现
在已经规定好了一个台阶的标准高度(规定标准台阶的高度 1),
我们就可以知道一到四楼所有台阶的相对高度。 因为腿长, wyh 一
次最多能迈四个标准台阶的高度。
因为 wyh 沉迷于和某外校大佬续火花以及毒瘤出题等活动无法
自拔,所以他把这个问题扔给了平时最爱喊 666 和人生赢家 wyh
的你。
输入描述 第一行两个整数 n,m。 n 表示 1 到 4 楼一共有多少台阶, m 表示会
告诉你其中多少阶台阶的相对高度(其他未告诉的默认为 1)。接
下来 m 行, 每行有两个整数 a,b。表示第 a 阶台阶的相对高度是
b。(b 可能为 1)
输出描述 输出为一行,两个整数,分别为有多少种可能的方案 少步。方案数对 19260817 取模。 和最少走多
样例输入 3 1 2 4
样例输出 1 3
数据范围及提示 30% 50% 1<=m<= 1<=m<=n<= n<=20 10
70% 1<= m <= n < =100
100% 1 =< m <= n < =100000;
dp[i]:到高度i的方案数;
求个前缀和,递推就可以了;
O(n);
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int MAXN=600001,mod=19260817;
int n,m,v,cnt,ans;
int dp[MAXN<<2],a[MAXN],sum[MAXN];
void solve()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) a[i]=1;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y),a[x]=y;
}
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
dp[0]=1;
for(int i=1;i<=n;i++)
{
if(a[i]==1)
dp[sum[i]]+=dp[sum[i]-1]+(sum[i]-2>=0?dp[sum[i]-2]:0)+(sum[i]-3>=0?dp[sum[i]-3]:0)+(sum[i]-4>=0?dp[sum[i]-4]:0);
else if(a[i]==2)
dp[sum[i]]+=dp[sum[i]-2]+(sum[i]-3>=0?dp[sum[i]-3]:0)+(sum[i]-4>=0?dp[sum[i]-4]:0);
else if(a[i]==3)
dp[sum[i]]+=dp[sum[i]-3]+(sum[i]-4>=0?dp[sum[i]-4]:0);
else if(a[i]==4)
dp[sum[i]]+=dp[sum[i]-4];
dp[sum[i]]%=mod;
}
for(int i=1;i<=n;i++)
{
if(a[i]+cnt<4) cnt+=a[i];
else if(a[i]+cnt==4) cnt=0,ans++;
else if(a[i]+cnt>4) cnt=a[i],ans++;
}
if(cnt) ans++;//特判,可能最后一组不足4;
printf("%d %d",dp[sum[n]]%mod,ans);
}
int main()
{
freopen("stairs.in","r",stdin);
freopen("stairs.out","w",stdout);
solve();
fclose(stdin);
fclose(stdout);
return 0;
}
T2:
https://www.luogu.org/problem/show?pid=2656
tarjan缩点重新建图跑最长路;
不过既然是DAG,拓扑排序会快一点;
tarjan:O(n+m);
topsort: O(n);
总复杂度: O(n);
WA了一个点;
我的错误:
1.起点为S,但topsort时加入了其他入度为0的点(幸亏数据良心,坑数据可能会全WA);
2.tarjan从S开始就足够了;(!!!)
3.输入时double型变量定义成了int;
4.忽略了一种情况;
如果用topsort,注意这种情况(数据没有卡QWQ):
如果记录了入度,上面的点是无法别更新到的;
或者加上85行这句代码,此时可以记录入度;
所以此时的topsort本质是个bfs求最短路(有向图);
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;
const int MAXN=500001;
int nxt[MAXN],fst[MAXN],low[MAXN],dfn[MAXN],scc[MAXN];//tarjan
int dis[MAXN],dp[MAXN];//topsort
int n,m,tot,tim,cnt,sta,ans=-1;
bool vis[MAXN];
struct hh
{
int from,to,cost;
double ji;
}mp[MAXN];
class ss
{
public :
int from,to,cost;
}ma[MAXN];
stack<int>S;
queue<int>q;
void build(int f,int t,int c,double s)
{
mp[++tot]=(hh){f,t,c,s};
nxt[tot]=fst[f];
fst[f]=tot;
return;
}
void add(int f,int t,int c)
{
ma[++tot]=(ss){f,t,c};
nxt[tot]=fst[f];
fst[f]=tot;
return;
}
void init()
{
memset(fst,0,sizeof(fst));
memset(nxt,0,sizeof(nxt));
tot=0;
return;
}
void tarjan(int x)
{
low[x]=dfn[x]=++tim;
S.push(x);
for(int i=fst[x];i;i=nxt[i])
{
int v=mp[i].to;
if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
else if(!scc[v]) low[x]=min(low[x],dfn[v]);
}
if(low[x]==dfn[x])
{
cnt++;
while(true)
{
int u=S.top();
S.pop(),scc[u]=cnt;
if(u==x) break;
}
}
return;
}
void rebuild()
{
init();
for(int i=1;i<=m;i++)
{
int f=mp[i].from,t=mp[i].to;
if(!scc[f] || !scc[t]) continue;
if(scc[f]!=scc[t])
add(scc[f],scc[t],mp[i].cost);
else if(scc[f]==scc[t])
{
int num=mp[i].cost;
double xi=mp[i].ji;
while(num)
{
dis[scc[f]]+=num;
num*=xi;
}
}
}
return;
}
void topsort()// =>bfs
{
q.push(scc[sta]),dp[scc[sta]]=dis[scc[sta]];
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=fst[x];i;i=nxt[i])
{
int v=ma[i].to;
dp[v]=max(dis[v]+dp[x]+ma[i].cost,dp[v]);
q.push(v);//直接push;
}
}
return;
}
void solve()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,z;
double c;
scanf("%d%d%d%lf",&x,&y,&z,&c);
build(x,y,z,c);
}
scanf("%d",&sta);
tarjan(sta);
rebuild();
topsort();
for(int i=1;i<=cnt;i++) ans=max(ans,dp[i]);
printf("%d",ans);
return;
}
int main()
{
freopen("mushroom.in","r",stdin);
freopen("mushroom.out","w",stdout);
solve();
return 0;
}
T3:
https://www.luogu.org/problem/show?pid=1709
趣题;
O(n)的标程有点难想;
我的做法:
用string的STL瞎搞;
还有种做法是开链表模拟,但容易被卡;
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int MAXN=5000001;
int n;
char s[MAXN];
int calc()
{
int l=0,r=1,k=0;
while(l<n && r<n)
{
k=0;
while(s[(l+k)%n]==s[(r+k)%n] && k<n) k++;
if(k==n) return l<r?l:r;
if(s[(l+k)%n]<s[(r+k)%n]) r=r+k+1;
else l=l+k+1;
if(l==r) r++;
}
return l<r?l:r;
}
void solve()
{
cin>>n;
for(int i=0;i<n;i++) cin>>s[i];
cout<<calc();
return;
}
int main()
{
freopen("command.in","r",stdin);
freopen("command.out","w",stdout);
solve();
fclose(stdin);
fclose(stdout);
return 0;
}
T4:
无脑写(chao)上题解:
推导:设沸腾温度为a
则第一杯温度为a,需要加热t1=a
第二杯可以中和的最高温度为a/2,需要加热t2=a/2
第三杯可以中和的最高温度为t3=(a/4+a)/2=5a/8,需要加热t3=3a/8
第四杯可以中和的最高温度为t4=((a/2+a/4)/2+a)/2=11a/16,需要加热t4=5/16
则t3/t2=3/4=1-1/4, t4/t3=5/6=1-1/6
继续推导得t(n+1)/t(n)=1-1/2n
注意精度;
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
double now=420000.0000,ans=0.00000000000;
int n;
void solve()
{
scanf("%d",&n);
now/=(double)n;
for(double i=1;i<=n;i++)
{
ans=ans+now;
now*=(1-0.5/i);
}
printf("%.2lf",ans);
}
int main()
{
solve();
return 0;
}