NENU ACM 13级训练赛 2014-12-06(福州大学第十一届程序设计竞赛)

福大的校赛,题目是中文的,省去了大家读题的时间。


A题:水题

每7天就有一个周六和周日,算一算就行了。

<span style="font-size:18px;">#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;

int main(){
    int n;
    while(~scanf("%d",&n)){
        int num = n/7;
        int left = n%7;
        int ans = num*2;
        if(left == 6) ans++;
        printf("%d\n",ans);
    }
    return 0;
}</span>

B题:规律
类似单调队列,其实就是个O(n)的递推水题,把样例推一推就清楚了
2 1 3 1 4
2*1 + 1*2 + 3*3                       (1)
         1*1 + 3*2 + 1*3              (2)
                   3*1 + 1*2 + 4*3    (3)
式子(1)为 13
式子(2)为 13 - 2*1 - (1+3) + 1*3 = 10
        即: 13 - (2+1+3) + 1*3 = 10
式子(3)为 10 - 1*1 - (3+1) + 4*3 = 17
        即: 10 - (1+3+1) + 4*3 = 17
可以发现,式子(i)式子(i-1)减去sum(x[i-m]~x[i])再加上新的项。
这样扫一遍取最大就可以了

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;

const int MAXN = 1000010;
int a[MAXN];
int sum[MAXN];
int n,m;

int main() {
    while(~scanf("%d%d",&n,&m)) {
        sum[0] = 0;
        for(int i=1; i<=n; i++) {
            scanf("%d",&a[i]);
            sum[i] = sum[i-1] + a[i];
        }
        int ans = -1;
        int tmp = 0;
        for(int i=1; i<=m; i++) {
            tmp += i*a[i];
        }
        ans = max(ans,tmp);
        for(int i=m+1; i<=n; i++) {
            tmp = tmp - (sum[i-1]-sum[i-m-1]) + m*a[i];
            ans = max(tmp,ans);
        }
        printf("%d\n",ans);
    }
    return 0;
}</span>

C题:DFS

由题可知,给出的图是一颗树。
设1为根,我们都知道对于树,若一条边只能走一次,则从根到各个节点的路径是唯一的,而且是最短的。
用邻接表建图,从根开始深搜进去,遍历每一个节点,在回溯的过程中如果是军队要走的路径,就把节点标记一下。
最后把这些被标记的结点上的敌人求个和,就是答案。

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>

#define mt(a,b) memset(a,b,sizeof(a))
using namespace std;

const int M = 100100;

bool vis[M];
int val[M];

struct G{
    struct E{
        int v,next;
    }e[M<<1];
    int le,head[M];
    void init(){
        le=0;
        mt(head,-1);
    }
    void add(int u,int v){
        e[le].v=v;
        e[le].next=head[u];
        head[u]=le++;
    }
}g;

int fa[M];
void dfs(int u){
    for(int i=g.head[u];~i;i=g.e[i].next){
        int v = g.e[i].v;
        if(fa[u]==v) continue;
        fa[v] = u;
        dfs(v);
        if(vis[v]){
            vis[u]=true;
        }
    }
}

int main(){
    int n,k;
    while(~scanf("%d%d",&n,&k)){

        mt(vis,false);
        mt(fa,-1);

        g.init();
        for(int i=1;i<=n;i++){
            scanf("%d",&val[i]);
        }
        int tmp;
        for(int i=1;i<=k;i++){
            scanf("%d",&tmp);
            vis[tmp] = true;
        }
        int x,y;
        for(int i=1;i<n;i++){
            scanf("%d%d",&x,&y);
            g.add(x,y);
            g.add(y,x);
        }

        dfs(1);

        int sum = 0;
        for(int i=1;i<=n;i++){
            if(vis[i]){
                sum += val[i];
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}</span>

D:DP

动态规划的重点就是定义出状态,已经状态的变化,据此写出状态转移方程之后,问题就迎刃而解了。
定义dp[i][j]表示到第i位时,有j位属于第一个串。
那么对于第i位的放法,就有放在第一个串和不放在第一个串这两种选择,然后更新数组,推到下一个状态。
注意推的过程中要模mod

为了节省空间,我采用了滚动数组,大概不用滚动数组也是能AC的,可以试试。

对于这个题目转移思想和滚动数组的使用,建议认真研究一下《背包九讲

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>

#define LL __int64
#define mt(a,b) memset(a,b,sizeof(a))

using namespace std;

const LL MOD = 1e9+7;
const int M = 3010;

char str[M];
LL dp[2][M];

int n;

int main() {
    int _;
    scanf("%d",&_);
    while(_--) {
        scanf("%d",&n);
        scanf("%s",str);
        mt(dp,0);
        dp[0][0] = 1;
        int now = 1;
        for(int i=0; i<2*n; i++) {
            mt(dp[now&1],0);
            if (str[i]=='B') {
                for (int j=0; j<=n; j++) {
                    //奇数
                    if (j&1)  {
                        dp[now&1][j+1] = (dp[now&1][j+1] + dp[i&1][j]) % MOD;

                    }
                    if ((i-j)&1) {
                        dp[now&1][j] = (dp[now&1][j] + dp[i&1][j]) % MOD;
                    }
                }
            } else {
                for (int j=0; j<=n; j++) {
                    if ((j&1) == 0) {
                        dp[now&1][j+1] = (dp[now&1][j+1] + dp[i&1][j]) % MOD;
                    }
                    if (((i-j)&1) == 0) {
                        dp[now&1][j] = (dp[now&1][j] + dp[i&1][j]) % MOD;
                    }
                }
            }
            now++;
        }
        printf("%I64d\n",dp[0][n]);
    }
    return 0;
}


E:线段树

很明显的线段树的题,采用lazy数组延迟标记,区间查询区间更新。

属于基础题,望多加巩固练习。

题目说有30%的小数据,可能暴力能过吧,我不清楚,我看见有人裸暴TLE,还是稳一点吧。

#include<cstdio>
#define lrrt int L,int R,int rt
#define iall 1,n,1
#define imid int mid=(L+R)>>1
#define lson L,mid,rt<<1
#define rson mid+1,R,rt<<1|1
const int M=1e5+10;
int a[M];
struct T {
    int sum,lazy;
} tree[M<<2];
void pushup(int rt) {
    tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
}
void build(lrrt) {
    tree[rt].lazy=0;
    if(L==R) {
        tree[rt].sum=a[L];
        return ;
    }
    imid;
    build(lson);
    build(rson);
    pushup(rt);
}
void pushdown(int mid,lrrt) {
    if(tree[rt].lazy) {
        tree[rt<<1].lazy+=tree[rt].lazy;
        tree[rt<<1|1].lazy+=tree[rt].lazy;
        tree[rt<<1].sum+=(mid-L+1)*tree[rt].lazy;
        tree[rt<<1|1].sum+=(R-mid)*tree[rt].lazy;
        tree[rt].lazy=0;
    }
}
int query(int x,int y,lrrt) {
    if(x<=L&&R<=y) return tree[rt].sum;
    imid;
    pushdown(mid,L,R,rt);
    int ans=0;
    if(mid>=x) ans+=query(x,y,lson);
    if(mid<y)  ans+=query(x,y,rson);
    return ans;
}
void update(int x,int y,int z,lrrt) {
    if(x<=L&&R<=y) {
        tree[rt].sum+=(R-L+1)*z;
        tree[rt].lazy+=z;
        return ;
    }
    imid;
    pushdown(mid,L,R,rt);
    if(mid>=x) update(x,y,z,lson);
    if(mid<y)  update(x,y,z,rson);
    pushup(rt);
}
int main() {
    int n,m,q,x;
    while(~scanf("%d%d%d",&n,&m,&q)) {
        for(int i=1; i<=n; i++) {
            scanf("%d",&a[i]);
        }
        build(iall);
        while(q--) {
            scanf("%d",&x);
            printf("%d\n",query(x,x+m-1,iall));
            update(x,x+m-1,-1,iall);
        }
    }
    return 0;
}


F题:待更新


G题:多源最短路+矩阵快速幂

题目要求从1点到n点的恰好k次最短路

可以很快想到floyd求出各个点之间的最短路,但是k很大,不能递推过去。

可以先求出1秒之后,各个点之间的最短路,得出一个矩阵,然后我们可以用这个矩阵继续推下去,推k-1次,这样就可以想到用矩阵快速幂了。

其实在这里,矩阵快速幂起到了加速的效果。

注意本题的矩阵快速幂并不是常规的矩阵快速幂,需要改写一下,具体看代码。

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>

#define LL __int64
#define mt(a,b) memset(a,b,sizeof(a))

using namespace std;

const LL inf = 0x3f3f3f3f3f3f3f3fLL;


class Matrix {
    typedef LL typev;
    static const int MV=55;
    friend Matrix operator *(const Matrix &a,const Matrix &b) {
        Matrix ret;
        ret.n=a.n;
        for(int i=0; i<a.n; i++) {
            for(int j=0; j<b.n; j++) {
                LL tmp = inf;
                for(int k=0; k<a.n; k++) {
                    tmp = min(tmp, a.val[i][k] + b.val[k][j]);
                }
                ret.val[i][j]=tmp;
            }
        }
        return ret;
    }
    friend Matrix operator ^ (Matrix a,LL b) {
        Matrix ret = a;
        while(b) {
            if(b&1) ret = a*ret;
            a = a*a;
            b>>=1;
        }
        return ret;
    }
public:
    int n,m;///n行m列
    typev val[MV][MV];
    void init(int tn) {
        mt(val,inf);
        n = tn;
    }
} A;

int main() {
    int n,h;
    LL k;
    int _;
    scanf("%d",&_);
    while(_--) {
        scanf("%d%d%I64d",&n,&h,&k);
        A.init(n);

        int u,v;
        LL w;
        while(h--) {
            scanf("%d%d%I64d",&u,&v,&w);
            u--;
            v--;
            A.val[u][v] = min(A.val[u][v],w);
        }

        Matrix ans = A^(k-1);

        if(ans.val[0][n-1] == inf) puts("-1");
        else printf("%I64d\n",ans.val[0][n-1]);

    }
    return 0;
}
</span>

H题:待更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这里是50种不同国家的语言表达“我爱你”: 1. English - I love you 2. Spanish - Te quiero 3. French - Je t'aime 4. German - Ich liebe Dich 5. Italian - Ti amo 6. Portuguese - Eu te amo 7. Russian - Ya lyublyu tebya 8. Chinese (Mandarin) - Wo ai ni 9. Japanese - Aishiteru 10. Korean - Sarang hae 11. Arabic - Ana behibak (to a male) / Ana behibek (to a female) 12. Dutch - Ik hou van je 13. Danish - Jeg elsker dig 14. Swedish - Jag älskar dig 15. Norwegian - Jeg elsker deg 16. Finnish - Minä rakastan sinua 17. Polish - Kocham Cię 18. Czech - Miluji Tě 19. Hungarian - Szeretlek 20. Bulgarian - Obicham te 21. Greek - S'ayapo 22. Turkish - Seni seviyorum 23. Romanian - Te iubesc 24. Croatian - Volim te 25. Serbian - Volim te 26. Slovenian - Ljubim te 27. Slovak - Milujem Ťa 28. Estonian - Ma armastan sind 29. Latvian - Es tevi mīlu 30. Lithuanian - Aš tave myliu 31. Icelandic - Ég elska þig 32. Albanian - Te dua 33. Thai - Phom rak khun (to a male) / Chan rak khun (to a female) 34. Vietnamese - Anh ye^u em (to a female) / Em ye^u anh (to a male) 35. Indonesian - Saya cinta padamu 36. Filipino - Mahal kita 37. Hindi - Main tumse pyar karta hoon 38. Bengali - Ami tomake bhalobashi 39. Urdu - Main tumse muhabbat karta hoon 40. Marathi - Me tujhe pyaar kartaa hoo 41. Punjabi - Main tere pyar da haan 42. Telugu - Nenu ninnu premisthunnanu 43. Tamil - Naan unnai kaadhalikken 44. Malayalam - Njan ninnodenikkoo 45. Kannada - Naanu ninna preetisuttene 46. Gujrati - Hu tane pyar karoo chu 47. Nepali - Ma timilai maya garchu 48. Sinhala - Mama oyata arderyi 49. Burmese - Chit pa de 50. Mongolian - Bi chamd hairtai
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值