[队内测试Day10.18]递推+tarjan+最小表示法+脑洞

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

luoguP2656采蘑菇

方法:发现一条路若能经过大于一次,则一定出现了强连通分量,则该强连通分量里所有路上的蘑菇我们都能采干净
故采用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

luoguP1709隐藏口令

最小表示法,正解见: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

luoguP1984烧水问题

%%%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;

最后递推求解。 

*/
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值