ACM训练日记—

        下午打了一场练习赛,从做D题来看读题不认真,复杂代码实现起来还是太慢。E题我的思路并不够灵活。一直纠结于DE两题,其实F题和H题都应该做出来的。    还有英语QAQ。

整理下今天的练习赛。

ZOJ—3939

      题意:当周一是在一个月的1,11,21日时被称为幸运日。给出一个日期是第一个幸运日,求第k个幸运日的日期。

因为数据量k<=1e9,所以必定是打表找规律。

      这个题打下表就能发现400年一循环的规律。

代码来自:https://blog.csdn.net/RaAlGhul/article/details/51237029

typedef long long ll;  
struct point  
{  
    int year, month, day;  
    int cnt;  
    point (int year = 0, int month = 0, int day = 0, int cnt = 0): year(year), month(month), day(day), cnt(cnt){}  
};  
point a[10000];  
int n;  
int cnt;  
int d1[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};  
int d2[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};  
bool check(int x)  
{  
    if (x % 400 == 0 || (x % 4 == 0 && x % 100 != 0)) return true;  
    return false;  
}  
void init()  
{  
    cnt = 1;  
    int day = 1;  
    for (int i = 1753; i <= 1753 + 399; i++)  
    {  
        bool flag = false;  
        flag = check(i);  
        for (int j = 1; j <= 12; j++){  
            int tmp = (flag == 1 ? d2[j] : d1[j]);  
            while (day <= tmp){  
                if (day == 1 || day == 11 || day == 21)  
                {  
                    a[cnt] = point(i, j, day, cnt);  
                    cnt++;  
                }  
                day += 7;  
            }  
            day -= tmp;  
        }  
    }  
}  
int main()  
{  
    init();  
    cnt--;  
    int t;  
    scanf("%d", &t);  
    while (t--){  
        int y, m, d;  
        scanf("%d%d%d", &y, &m ,&d);  
        int n;  
        scanf("%d", &n);  
        n--;  
        int ind = n / cnt;  
        int tmp = n - ind * cnt;  
        int mid = 0;  
        while (y >= 1753 + 400)  
        {  
            y -= 400;  
            mid++;  
        }  
        //printf("y = %d mid = %d tmp = %d\n", y, mid, tmp);  
        ll t1 = 0, t2 = 0, t3 = 0;  
        for (int i = 1; i <= cnt; i++){  
            if (y == a[i].year && m == a[i].month && d == a[i].day){  
                int g = i;  
                g += tmp;  
                while (g > cnt){  
                    g -= cnt;  
                    mid++;  
                }  
                t1 = (ll)a[g].year + (ll)(mid + ind) * 400;  
                t2 = (ll)a[g].month;  
                t3 = (ll)a[g].day;  
                break;  
            }  
        }  
        printf("%lld %lld %lld\n", t1, t2, t3);  
    }  
    return 0;  
}  

       zoj3940—Modulo Query

题意:

F(1, X) = X mod A1.
F(i, X) = F(i - 1, X) mod Ai (2 ≤i≤N)

 求F(N,X)=y,给出y,求有多少个X(X<=m)满足F(N,X)=y。

其实这道题就是求((X mod a1)mod a2)...)mod an=y。求X有多少个X(X<=M)满足条件。

      这道题思路绝对是脑洞清奇。开拓思路。就是0到M这每个区间进行叠加,比如(1,M)的区间上所有数对a1取模,所得数必定在(0,a1-1)之间,下面可以快速算出大约有多少个区间变成了(0,a1-1),继续依次取模a2...。

这是官方题解:

可以观察到一个区间对一个数取模后的结果也可以用区间来表示,并且这些区间的左端点都是0。于是直接用一个map存每次取模之后的区间和这个区间出现次数,要取模的时候,找出所有右端点大于等于当前模数的所有区间,暴力算一下结果即可。对于查询,二分下位置,求个后缀和就好了。下面分析下复杂度为什么是对的。

众所周知:一个数对一堆数取模,最多会有\loglog次值的改变。对于这题每对一个新数取模,最多只会增加一个区间。考虑当前区间是[0,v_i][0,vi],要对xx取模,那么对于v_i < xvi<x的区间没有变化,对于v_i \ge xvix的那些区间会变成[0,v_i \text{ mod } x][0,vi mod x],并且可能会新增区间[0,x-1][0,x1]。这个过程其实相当于每个a_iai对后面所有数依次取模,那么总共会有O(n \log n)O(nlogn)次值的改变,每次改变需要在map上修改一下,总复杂度是O(n\log^2n)O(nlog2n)的。

 代码:https://blog.csdn.net/below_crusder/article/details/51277936

#define maxn 500005  
const int mod = 1e9+7;  
int sum[maxn];  
int ans[maxn];  
struct node// 区间[0, right]以及该区间出现的次数cnt  
{   
    int right, cnt;  
    node(int a = 0, int b = 0)  
    {  
        right = a,cnt = b;  
    }  
    bool operator<(const node &a) const  
    {  
        return right < a.right;  
    }  
}tp[100005];  
int main()  
{  
    int t;  
    cin>>t;  
    while(t--)  
    {  
        int n, m, s;  
        cin>>n>>m;  
        priority_queue<node> que; //优先队列  
        que.push(node(m, 1)); //入队  
        while(n--)  
        {  
            cin>>s; 
            while(que.top().right>=s)  //将区间右断点大于s的区间进行mod处理  
            {  
                node tmp=que.top();       
                que.pop();  
                while(!que.empty()&&que.top().right==tmp.right)   //如果有重复的区间进行整合  
                    tmp.cnt+=que.top().cnt,que.pop();  
                que.push(node(s-1,(tmp.right+1)/s*tmp.cnt));  //将处理过后的区间入队  
                if((tmp.right + 1)% s)  
                    que.push(node(tmp.right%s, tmp.cnt));  //如果一个区间不能被整除  将多余的区间入队  
            }  
        }  
        int cnt = 1;  
        while(!que.empty()) //将最后得到的所有区间存在一个数组中  
        {  
            node tmp = que.top();  
            que.pop();  
            while(!que.empty() && que.top().right == tmp.right)  
                tmp.cnt += que.top().cnt, que.pop();  
            tp[cnt++]=tmp;  
        }  
        sort(tp+1,tp+cnt); //将各个区间进行从小到大排序  
        memset(sum, 0, sizeof sum);  //初始化为0  
        for(int i = 1; i < cnt; i++)  
        {  
            ans[i] = tp[i].right;  
            sum[cnt - i] = sum[cnt - i + 1] + tp[cnt - i].cnt;  
        }  
        int q;  
        cin>>q;  
        long long out=0;  
        for(int i=1;i<=q;i++)  
        {  
            cin>>s;  
            int pos=lower_bound(ans+1,ans+cnt,s)-ans; //二分查找结果  
            out=(out+1ll*i*sum[pos]%mod)%mod;
        }  
        cout<<out<<endl;  
    }  
    return 0;  

     Zoj—3941

题意:给出k是持续兴奋的天数,给出若干个区间作为可以选择的开始点,求怎么样选可以兴奋总天数最多。

思路1:先合并区间,从第一个区间的第一个点开始放,放1,k+1,,当下一个点放的区间右端在区间中不用管,继续向后放。如果不在,记录一下如果放可以得到多少。放完以后如果还有剩余在把存储的依次大到小拿出来。

思路2:当遇见右端点不在区间中时,dfs两个方向,放前一个的,放后一个的左端。

 推荐博客:https://blog.csdn.net/snowy_smile/article/details/51253003

     Zoj—3944

就是大暴力,暴力找该点不是“.”的就消一下周围满足条件的。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值