【瓜分5000元獎金】Wannafly挑戰賽13題目題解

A-zzy的小號

/*****************************
Author Ms. Wen
Date 2018/4/6

解題思路:
i,l,I,L   等價  遇到任意一個*4
o,O,0     等價  遇到任意一個*3
大小寫字母等價  遇到任意一個*2
*******************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

const int mod = 1e9+7;
char str[1000000];
int main() {
    while(~scanf("%s",str)) {
        long long ans = 1;
        for(int i = 0; i < (int)strlen(str); i++) {
            if(str[i]>='a' && str[i]<='z') {
                if(str[i]=='l' || str[i]=='i') {
                    ans = ans*4%mod;
                }
                else if(str[i]=='o') {
                    ans = ans*3%mod;
                }
                else {
                    ans = ans*2%mod;
                }
            }
            else if(str[i]>='A'&&str[i]<='Z') {
                if(str[i]=='I' || str[i]=='L') {
                    ans = ans*4%mod;
                }
                else if(str[i]=='O') {
                    ans = ans*3%mod;
                }
                else {
                    ans = ans*2%mod;
                }
            }
            else if(str[i] == '0'&&str[i]<='9') {
                ans = ans*3%mod;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

B-Jxc的軍訓

/*****************************
Author Ms. Wen
Date 2018/4/6

解題思路:
逆向思維,被曬到的概率等於1-不被曬到的概率。
只要站在雲下不管太陽在哪都不會被曬到。則
不被雲曬到的概率爲 m/(n*n)。則被曬到的概率是
(n*n-m)/(n*n)。然後剩餘的就是求乘法逆元的事情
了,由於取模的數爲素數,因此分母fm的乘法逆元
就是fm^(mod-2)。在題目中我們經常看到概率的答案
很大,其實就是因爲這些概率通常牽涉取模,與乘法
逆元有關,不必大驚小怪。
*******************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cmath>

using namespace std;

const int mod = 998244353;

long long qucik(int n,int k) {
    long long ans,res;
    ans = 1;
    res = (long long)n;
    while(k) {
        if(k&1) {
            ans = ans*res%mod;
        }
        res = res*res%mod;
        k = k/2;
    }
    return ans;
}
int main() {
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        int fz = (n*n-m)%mod;
        int fm = (n*n)%mod;
        long long ans = fz*qucik(fm,mod-2)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

C-zzf的好矩陣

/*****************************
Author Ms. Wen
Date 2018/4/6

解題思路:
比如3*3的矩陣,可以通過一定操作放成
1 2 3
4 5 6
7 8 9
由於矩陣中的數字大於0且小於等於n*n,且各個
位置上數不同。則矩陣中的數字必是1~n*n中的數字
各一個。經過菸草發現。1 4 7,2 3 8,3 6 9 .
必是一列。1 2 3,4 5 6,7 8 9,必是一列,因此只要
固定1,2,3的位置,與其綁定的數字必綁定。
1 ,2 ,3的排列數都3!種。橫排的數還可以挑選自己
所處的行,又是3!種。而上例中所有在一行的可以換
成在一列。
1 4 7
2 5 8
3 6 9
因此n*n的矩陣,總共有n!*n!*2种放置方法。
*******************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cmath>

using namespace std;

const int mod = 998244353;
typedef long long ll;
int main() {
    int p;
    while(~scanf("%d",&p)) {
        ll ans = 1;
        for(ll i = 1; i <= p; i++) {
            ans = ans*i%mod;
        }
        ans = ans*ans%mod;
        ans = ans*2%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

D-applese的生日

/***************************************************************
Author Ms. Wen
Date 2018/4/6

思路:題解上說得挺清楚。
首先有一個結論:每塊蛋糕分成的每一塊的大小是相同的 基於這個結論,
每次找到當前劃分最大塊所在的大塊並將其劃分數+1,檢查是否滿足題目的
要求考慮這個結論爲什麼是正確的考慮一個蛋糕切的刀數不變,那麼可以想
到假如分割得到的塊是不同的,那麼可能的貢 獻是增加最大和最小之間的差
值,那麼這樣答案只會更劣,所以可以想到分成的每個塊的大小是相同的。
*******************************************************************/
#include <iostream>
#include <stdio.h>
#include <map>

using namespace std;

const int maxn = 1005;
double w[maxn];
int divs[maxn];  //蛋糕的分塊數。
multimap<double,int>m; //multimap中key的值可以重複
multimap<double,int>::iterator it1,it2;
int main() {
    double t;
    int n;
    cin>>t>>n;
    m.clear();
    for(int i = 1; i <= n; i++) {
        cin>>w[i];
        divs[i] = 1;
        m.insert(make_pair(w[i],i));
    }
    it1 = m.begin();
    it2 = --m.end();
    int ans = 0;
    while(it1->first/it2->first < t) {
        int id = it2->second;
        divs[id]++;
        double aver = w[id]/divs[id];
        m.erase(it2);
        m.insert(make_pair(aver,id));
        it1 = m.begin();
        it2 = --m.end();
        ans++;
    }
    printf("%d\n",ans);
    return 0;
}

E-VVQ與線段

/**************************************
Author Ms. Wen
Date 2018/4/7

解題思路:
對於所有線段按照左端點從小到大排序。
對於一條選定的線段x1,y1。需要考慮左端點大於等於x1且小於等於y1的
所有線段x2,y2
1.考慮Seg(x2,y2)與Seg(x1,y1)相交但不包含在Seg(x1,y1)中,則異或值
爲(x2+y2)-(x1+y1).由於x1+y1固定,則需要使x2+y2最大。因此建立線段
樹維護所有線段left+right的最大值。
2.考慮Seg(x2,y2)包含在Seg(x1,y1)中,則異或值爲(y1-x1)-(y2-x2)。
由於y1-x1固定,則需要使y2-x2最小,因此建立線段樹維護所有線段right-left
的最小值。
求這兩種情況的最大值。即爲答案。

變量含義:
Max:維護線段left+right最大值的數組
Min:維護線段right-left最小值的數組
s:存放線段信息

方法含義:
push_up1(root):用於更新Max數組的值
push_up2(root):用於更新Min數組的值
build1:構造維護left+right的線段樹
build2:構造維護right-left的線段樹
query1:求與當前線段相交的線段中left+right值最大的。
query2:求與包含在當前線段中的線段中right-left值最小的。
**********************************************************/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#define lchild left,mid,root<<1
#define rchild mid+1,right,root<<1|1

using namespace std;

const int maxn = 200000+4;
int Max[maxn<<2];   //維護線段l+r的最大值
int Min[maxn<<2];   //維護線段r-l的最小值
struct Segment {
    int left;
    int right;
    friend bool operator<(Segment s1,Segment s2) {
        return s1.left<s2.left;
    }
}s[maxn];
void push_up1(int root) {
    Max[root] = max(Max[root<<1],Max[root<<1|1]);
}
void push_up2(int root) {
    Min[root] = min(Min[root<<1],Min[root<<1|1]);
}
//維護l+r的最大值,處理相交情況
void build1(int left,int right,int root) {
    if(left==right) {
        Max[root] = s[left].left + s[left].right;
        return;
    }
    int mid = (left+right)>>1;
    build1(lchild);  //遞歸構建左右子樹
    build1(rchild);
    push_up1(root);
}
//維護r-l的最小值,處理包含的情況
void build2(int left,int right,int root){
    if(left==right) {
        Min[root] = s[left].right - s[left].left;
        return;
    }
    int mid = (left+right)>>1;
    build2(lchild);
    build2(rchild);
    push_up2(root);
}
int query1(int L,int R,int left,int right,int root) {
    if(L<=left && right<=R) {
        return Max[root];
    }
    int mid = (left+right)>>1;
    int ans = 0;
    if(L<=mid) ans = max(ans,query1(L,R,lchild));
    if(R>mid) ans = max(ans,query1(L,R,rchild));
    return ans;
}
int query2(int L,int R,int left,int right,int root) {
    if(L<=left && right<=R) {
        return Min[root];
    }
    int mid = (left+right)>>1;
    int ans = 1e8;
    if(L<=mid) ans = min(ans,query2(L,R,lchild));
    if(R>mid)  ans = min(ans,query2(L,R,rchild));
    return ans;
}
int main() {
    int n,l,r;
    while(~scanf("%d",&n)) {
        for(int i = 1; i <= n; i++) {
            scanf("%d%d",&l,&r);
            s[i].left = min(l,r);  //防止有l>r的坑
            s[i].right = max(l,r);
        }
        sort(s+1,s+n+1);  //按照左端點,從小到大排序。
        build1(1,n,1);
        build2(1,n,1);
        Segment tmp;
        tmp.right = 0;
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            tmp.left = s[i].left;
            l = lower_bound(s+1,s+n+1,tmp)-s;   //尋找左端點>=當前線段的第一條線段
            tmp.left = s[i].right;
            r = upper_bound(s+1,s+n+1,tmp)-s-1; //尋找左端點>當前右端點的第一條線段。
            if(l > r) continue;
            //則下標(l,r)內的線段都與當前線段相交或被包含其中。
            int num1 = query1(l,r,1,n,1);
            int num2 = query2(l,r,1,n,1);
            ans = max(ans,num1-(s[i].left+s[i].right));
            ans = max(ans,(s[i].right-s[i].left)-num2);
        }
        printf("%d\n",ans);
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章