T1
来自dfkd的半原创题
灵感来自codevs爬楼梯
大意:
给出n,m,表示有n层楼梯,m层楼梯高度不确定(不超过4),给出m个楼梯的位置及相对高度,其余楼梯相对高度为1
一次最多可迈4的相对高度
求有多少种方案以及最小步数
设dp[i]为走到第i层的方案数,f[i]为走到第i层的最小步数
若一次可迈两层,则dp[i]可由dp[i - 1]及dp[i - 2]推来
若可迈四层,dp[i]可由dp[i - 1]~dp[i - 4]推来
同理,f[i]=min(f[i-1]~f[i-4])+1;
这里的数字指相对高度
但我们发现,这些高度是不一定成立的
譬如当前台阶高为4,则它不可能由i - 1推来
处理方法:拆分台阶为相对高度
exist[i]表示这一高度处是否存在台阶
转移时,若exist[x] = false,则跳过
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
#define mod 19260817
using namespace std;
const int MAXN = 400000 + 50;
LL h[MAXN],n,m,x,tot,dp[MAXN],f[MAXN];
bool exist[MAXN];
int main(){
freopen("stairs.in","r",stdin);
freopen("stairs.out","w",stdout);
scanf("%lld%lld",&n,&m);
for(int i = 1;i <= m;i++){
scanf("%lld",&x);
scanf("%lld",&h[x]);
}
for(int i = 1;i <= n;i++){
if(!h[i])h[i] = 1;
while(h[i]){
h[i] --;
tot ++;
}
exist[tot] = true;
}
exist[0] = true;
dp[0] = 1;
memset(f,0x3f,sizeof(f));
f[0] = 0;
for(int i = 0;i <= tot;i++){
if(!exist[i])continue;
for(int j = 1;j <= 4;j++){
if(!exist[i + j])continue;
dp[i + j] = (dp[i]%mod + dp[i + j]%mod)%mod;
f[i + j] = min(f[i + j],f[i] + 1);
}
}
printf("%lld %lld",dp[tot],f[tot]);
return 0;
}
T2
方法:发现一条路若能经过大于一次,则一定出现了强连通分量,则该强连通分量里所有路上的蘑菇我们都能采干净
故采用tarjan缩点+重新建图,将分量中的蘑菇数赋给到达它的边
然后最长路
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stack>
#include<queue>
#define LL long long
#define INF 1061109567
using namespace std;
const int MAXN = 200000 + 50;
int n,m;
struct edge{
int f,t,v;
double w;
}l[MAXN << 1],e[MAXN << 1];
int next[MAXN << 1],head[MAXN],tot,dis[MAXN];
int second[MAXN << 1],first[MAXN << 1],cnt;
void init(int n){
for(int i = 1;i <= n;i++){
head[i] = -1,first[i] = -1,dis[i] = -INF;
}
}
void build(int f,int t,int v,double w){
l[++tot] = (edge){f,t,v,w};
next[tot] = head[f];
head[f] = tot;
}
int a,b,c;
double w;
int s;
int dfn[MAXN],sccn[MAXN],low[MAXN],dfn_cnt,sccn_cnt,sz[MAXN];
int W[MAXN];
stack <int> q;
void dfs(int u){
low[u] = dfn[u] = ++dfn_cnt;
q.push(u);
for(int i = head[u];i != -1;i = next[i]){
int v = l[i].t;
if(!dfn[v]){
dfs(v);
low[u] = min(low[u],low[v]);
}
else if(!sccn[v]){
low[u] = min(low[u],dfn[v]);
}
}
if(low[u] == dfn[u]){
++sccn_cnt;
while(!q.empty()){
int x = q.top();
q.pop();
sz[sccn_cnt]++;
sccn[x] = sccn_cnt;
if(x == u)break;
}
}
}
int check(int num){
int temp = l[num].v,tot = 0;
while(temp){
tot += temp;
temp *= l[num].w;
}
return tot;
}
void solve(int s){
for(int u = 1;u <= n;u ++){
for(int i = head[u];i != -1;i = next[i]){
int t = l[i].t;
if(sccn[u] == sccn[t]){
W[sccn[u]] += check(i);
}
}
}
}
void rebuild(int f,int t,int v){
e[++cnt] = (edge){f,t,v};
second[cnt] = first[f];
first[f] = cnt;
}
queue <int> que;
int ans = 0;
bool inq[MAXN];
void spfa(int x){
dis[x] = 0;
inq[x] = true;
que.push(x);
while(!que.empty()){
int u = que.front();
que.pop();
inq[u] = false;
for(int i = first[u];i != -1;i = second[i]){
int t = e[i].t;
if(dis[t] < dis[u] + e[i].v){
dis[t] = dis[u] + e[i].v;
if(!inq[t])que.push(t);
inq[t] = true;
ans = max(ans,dis[t]);
}
}
}
}
int main(){
freopen("mushroom.in","r",stdin);
freopen("mushroom.out","w",stdout);
scanf("%d%d",&n,&m);
init(n);
for(int i = 1;i <= m;i++){
scanf("%d%d%d%lf",&a,&b,&c,&w);
build(a,b,c,w);
}
scanf("%d",&s);
dfs(s);
solve(s);
for(int u = 1;u <= n;u ++){
for(int i = head[u];i != -1;i = next[i]){
int t = l[i].t;
if(sccn[t] != sccn[u]){
rebuild(sccn[u],sccn[t],l[i].v + W[sccn[t]]);
}
}
}
spfa(sccn[s]);
printf("%d",ans + W[sccn[s]]);
fclose(stdin);
fclose(stdout);
return 0;
}
T3
最小表示法,正解见:http://m.blog.youkuaiyun.com/Lin1043/article/details/75213855
洛谷上rand到这题的时候考虑的链表
首先将串中最小的字符挂起来,保证答案一定在这些位置中;
然后依次比对他们的下位、下下位,每次比对将不是最小字符的位置删除
当链表中仅剩一个元素时,该位置为答案
随机串中的理想复杂度log26(L),快于双指针;
但最怕的情况
aaaaaaaaaaaaaaabbb………
仅有末尾字符不同,会被卡n^2
然后洛谷上的数据就辣么卡导致我只能过两个点……
随机串中,10^6可以轻松过
链表代码:
//链表
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN = 5000000 + 50;
int n;
char s[MAXN];
int sl[MAXN],cnt;
char last = 'z';
bool del[MAXN];
int first,next[MAXN],pre[MAXN],num[MAXN];
int main()
{
freopen("command.in","r",stdin);
freopen("command.out","w",stdout);
scanf("%d",&n);
scanf("%s",s + 1);
for(int i = 1;i <= n;i ++)
{
if(s[i] < last)last = s[i];
}
for(int i = 1;i <= n;i ++)
{
if(s[i] == last)sl[++ cnt] = i,num[cnt] = i;//把串里第一遍找到的最小的挂起来
}
first = 0;
next[first] = 1;
pre[1] = first;
for(int i = 1;i <= cnt;i ++)
{
next[i] = i + 1;
pre[i] = i - 1;
}
next[cnt] = -1;
while(cnt != 1)
{
last = 'z';
int p;
for(p = next[first];p != -1;p = next[p])
{
sl[p] ++;
if(sl[p] > n)sl[p] -= n;
if(s[sl[p]] < last)last = s[sl[p]];
}
for(p = next[first];p != -1;p = next[p])
{
if(s[sl[p]] > last)//每扫一遍把不是最小的删去
{
next[pre[p]] = next[p];
pre[next[p]] = pre[p];
cnt --;
}
}
}
printf("%d",num[next[first]] - 1);
fclose(stdin);
fclose(stdout);
return 0;
}
数据很私心的给了链表70分
T4
%%%maple
公式没推出来,就算打也只能n^2
于是挂std:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n;
double a=4200,k,ans=0,x=100;
int main()
{
freopen("water.in","r",stdin);
freopen("water.out","w",stdout);
scanf("%d",&n);
k=(double) 1/n;
ans+=a*x*k;
for(int i=2;i<=n;++i)
{
x*=(double)(2*(i-1)-1)/(2*(i-1));
ans+=a*x*k;
}
printf("%.2lf",ans);
return 0;
}
/*
最大的情况就是使已经烧开的水的热量被尽可能的利用。
我们发现,当一杯水的热量一个个的往右传递下去的话,热量最不容易浪费。
** 热量的传递 实际数据解释:
假设有5杯水: 0 0 0 0 0
第一杯水: 100 0 0 0 0 --> 6.25 50 25 12.5 6.25
第二杯水: 6.25 100 25 12.5 6.25--> 6.25 21.875 62.5 37.5 21.875
第三杯水: 6.25 21.875 100 37.5 21.875-->6.25 21.875 45.3125 68.75 45.3125
第四杯水: 6.25 21.875 45.3125 100 45.3125--> 6.25 32.875 45.3125 72.65625 72.65625
第五杯水:...... 100 。
我们发现 这五杯水被烧开前只进行热传递可以达到的温度为 0 50 62.5 68.75 72.65625
还需要升高的温度为: 100 50 37.5 31.25 27.34375
发现: 50/100=1/2 、37.5/50=3/4 、31.25/37.5=5/6、27.34375/31.25=7/8
规律:第i杯水需要上升的温度为第i-1杯水需要上升的温度* (2*(i-1)-1)/(2*(i-1)).
**热量的传递 公式解释(摘自洛谷题解) :
推导:设沸腾温度为a
//则第一杯温度为a,需要加热t1=a
//第二杯可以中和的最高温度为a/2,需要加热t2=a/2
//第三杯可以中和的最高温度为t3=(a/4+a)/2=5a/8,需要加热t3=3a/8
//第四杯可以中和的最高温度为t4=((a/8+5a/8)/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;
最后递推求解。
*/

1020

被折叠的 条评论
为什么被折叠?



