“美登杯”上海市高校大学生程序设计邀请赛 (华东理工大学)

补题链接:https://acm.ecnu.edu.cn/contest/173/

已更新题解:A B D E H

正在啃的:C


Problem A

解题思路:

假设取了【l,r】的字符,直接用12345代替对应编号的字符

        2       3       4      5

12      23     34     45

123    234   345

1234  2345

12345

按照这样一种一种颜色取,即1,2,12,3,23,123...

最后可以全部取到,所以答案就是n*(n+1)/2

代码:

#include<iostream>
#include<string>
#define x (r-l+1)
using namespace std;

int main()
{
    int n,q,l,r;
    string s;
    cin>>n>>q;
    cin>>s;
    while (q--){
        cin>>l>>r;
        cout << x*(x+1)/2 << endl;
    }
    return 0;
}

总结:看到有这么多人做出请果断判断这是签到题,一定有规律!


Problem B

解题思路:

暴力取所有正向的三角形的三个顶点和倒着的三角形的三个顶点,排序后扔一个三个字符的哈希值进set,最后统计个数。

也就是暴力枚举。

代码:

#include<cstdio>
#include<algorithm>
#include<set>

using namespace std;

const int N = 105;

char a[N][N];
set<int>s;

int main()
{
    int n;
    while (~scanf("%d",&n)){
        s.clear();
        n++;
        for (int i=1;i<=n;i++) scanf("%s",a[i]+1);
        ///找出所有正三角形
        for (int i=1;i<n;i++){
            for (int j=1;j<=i;j++){
                int k = 1;
                while (i+k<=n){
                    //printf("i+k=%d j+k=%d\n",i+k,j+k);
                    int temp[3];
                    temp[0] = a[i][j]-'a'+1;
                    temp[1] = a[i+k][j]-'a'+1;
                    temp[2] = a[i+k][j+k]-'a'+1;
                    //printf("三个数分别是:%d %d %d\n",temp[0],temp[1],temp[2]);
                    sort(temp,temp+3);
                    s.insert(temp[0]*27*27+temp[1]*27+temp[2]);
                    k++;
                }
            }
        }
        //printf("正向的%d个\n",s.size());
        ///找出所有负三角形
        for (int i=n;i>=2;i--){
            for (int j=2;j<=i-1;j++){
                int k = 1;
                while (i-k>=j && j-k>=1){
                    int temp[3];
                    temp[0] = a[i][j]-'a'+1;
                    temp[1] = a[i-k][j]-'a'+1;
                    temp[2] = a[i-k][j-k]-'a'+1;
                    //printf("三个数分别是:%d %d %d\n",temp[0],temp[1],temp[2]);
                    sort(temp,temp+3);
                    s.insert(temp[0]*27*27+temp[1]*27+temp[2]);
                    k++;
                }
            }
        }
        printf("%d\n",s.size());
    }
    return 0;
}

总结:想想当时为什么没有做出来呢,还是因为不够冷静下来思考,反而去做看上去做得出实际上自己都不知道错哪的题,这是感性的分析;自己对于第一时间没有思路的题目就觉得很难--放弃,第一眼有思路,虽然思路可能错--往死里做,这是理性的分析。概括的说,菜!


Problem D

解题思路:

如果当前的起点是大于1的,一定可以推出一种先手必胜的策略,自己倒序推就行。

而如果当前起点等于1,那么顺着走下去直到不是1的,开始取那大于1的那堆的那个人必胜。

结论:先取到大于1的那堆的那个人胜利。

特殊处理一下全是1的。

剩下的任务就是判断起点等于1时谁先取到不是1,。

于是构造nt[ ]数组来取得以i为起点时下一次大于1的堆,用队列O(N)复杂度实现。

代码:

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>

using namespace std;

const int N = 1e5+5;

queue<int>que;
int nt[N],p[N];
bool flag;

int main()
{
    int n;
    while (~scanf("%d",&n)){
        flag = false;///是否有大于1的堆
        memset(nt,0,sizeof nt);
        for (int i=1;i<=n;i++){
            scanf("%d",p+i);
            if (p[i]>1)flag = true;
        }
        if (!flag){///只有1的堆特殊处理
            if (n%2==1) while (n--) printf("First\n");
            else while (n--) printf("Second\n");
            continue;
        }
        for (int i=1;i<=n;i++){
            if (p[i]>1){
                while (!que.empty()){
                    nt[que.front()] = i;
                    que.pop();
                }
            }
            else que.push(i);
        }
        while (!que.empty()){/// 处理最后一个大于1的数后面的1
            nt[que.front()] = p[1]==1? nt[1]:1;
            que.pop();
        }
        for (int i=1;i<=n;i++){
            if (nt[i]==0) printf("First\n");
            else {
                int cnt = i>nt[i]? n-i+nt[i]:nt[i]-i; ///cnt表示两点的间距,一个要分两段算,一个直接算
                if (cnt%2==0) printf("First\n");
                else printf("Second\n");
            }
        }
    }
    return 0;
}

总结:碰到的Nim有这么几种类型了:

①n堆,随意取一堆指定数量   -->sg+找sg规律

②按顺序取堆的无限制的 -->找规律,讲究思维

Nim要AC应该需要胆子大敢猜吧。


Problem E

解题思路:

①用欧拉筛预处理一百万的最小质因子

②线段树维护两个东西,一个是区间是否都是1 ud1,一个是乘以最小质数的次数 mtag。

我们知道:一个数乘以最小质数得到的数的最小质数还是本身,所以累乘过程直接做标记了。

③对于每次修改,如果是操作1,直接区间整体mtag++,用到哪儿下放到哪

如果是操作2,当当前区间mtag>0时,直接区间整体mtag--,如果不是,那么可能有些点将被单点暴力修改

④对于每次询问,用快速幂

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define debug(x) printf("----Line %s----\n",#x)
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define mid int m = l+r>>1
#define mod 1000000007

using namespace std;

const int N = 1e5+5,M = 1e6+5;

int prime[N],tot;///质数
ll mp[M];///mp[i]表示i的最小质因子
bool vis[M];///欧拉筛用来标记被筛掉的
bool ud1[N<<2];
int mtag[N<<2];
ll v[N];///省点空间,反正没有必要维护区间和

void push_up(int rt)
{
    ud1[rt] = ud1[rt<<1] && ud1[rt<<1|1];
}

void push_down(int rt)
{
    mtag[rt<<1] += mtag[rt];///加号别忘了-_-||
    mtag[rt<<1|1] += mtag[rt];
    mtag[rt] = 0;
}

void update(int L,int R,int op,int rt,int l,int r)///操作1,2
{
    if (ud1[rt]) return ;
    if (op==1 && L<=l && r<=R){
        mtag[rt]++;
        return ;
    }
    if (op==2 && L<=l && r<=R && mtag[rt]){
        mtag[rt]--;
        return ;
    }
    if (l==r){ ///操作2的暴力修改
        v[l] = v[l]/mp[v[l]];
        if (v[l]==1) ud1[rt] = true;
        return ;
    }
    if (mtag[rt]) push_down(rt);
    mid;
    if (L<=m) update(L,R,op,lson);
    if (R>m) update(L,R,op,rson);
    push_up(rt);
}

void getmp()///欧拉筛
{
    int tot = 0;
    mp[1] = 1;
    for (int i = 2;i<=1000000;i++){
        if (!vis[i]){
            prime[tot++] = i;
            mp[i] = i;
        }
        for (int j=0;j<tot && i*prime[j]<=1000000;j++){
            vis[i*prime[j]] = true;
            mp[i*prime[j]] = prime[j];
            if (i%prime[j]==0) break;
        }
    }

}

void build(int rt,int l,int r)///初始化 ud1
{
    if (l==r){
        if (v[l]==1) ud1[rt] = 1;
        return ;
    }
    mid;
    build(lson);
    build(rson);
    push_up(rt);
}

ll fast_power(ll a,int b)///快速幂
{
    ll ans = a;
    ll sb = mp[a];
    while (b){
        if (b%2==1) ans = (ans*sb)%mod;
        b>>=1;
        sb = sb*sb%mod;
    }
    return ans;
}

int query(int p,int rt,int l,int r)///得到p点的mtag的下标
{
    if (l==r){
        return rt;
    }
    if (mtag[rt]) push_down(rt);
    mid;
    if (p<=m) return query(p,lson);
    else return query(p,rson);
}

int main()
{
    getmp();
    int n,q,op,l,r,p;
    scanf("%d %d",&n,&q);
    for (int i=1;i<=n;i++) scanf("%lld",v+i);
    build(1,1,n);
    while (q--){
        scanf("%d",&op);
        if (op==1 || op==2){
            scanf("%d %d",&l,&r);
            update(l,r,op,1,1,n);
        }
        else {
            scanf("%d",&p);
            int pos = query(p,1,1,n);
            printf("%lld\n",fast_power(v[p],mtag[pos]));
        }
    }
    return 0;
}

总结:眼神死盯着区间为1剪枝,然后暴力修改。第九组数据T成狗。

后来看了大佬的代码发现原来可以区间++,又发现区间--。

感觉自己还是没有掌握线段树的精髓,以后做到线段树题目多问问自己可不可以区间修改。


Problem H

谢谢你让我没爆零,签到题大哥!

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define debug(x) printf("----Line %s----\n",#x)

using namespace std;

const int N = 1e5+5;

int n,m,p,x,y,l,r,ans,cnt;

int main()
{
    ll n,a,b,c,d,v;
    scanf("%lld %lld %lld %lld %lld",&n,&a,&b,&c,&d);
    ll ans = 0;
    for (int i=1;i<=n;i++) scanf("%lld",&x),ans+=x;
    if (ans>=a && ans>=c) ans = min(ans-b,ans-d);
    else if (ans>=a) ans = ans-b;
    else if (ans>=c) ans = ans-d;
    printf("%lld\n",ans);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值