1. 程式人生 > >Wannafly挑戰賽22遊記

Wannafly挑戰賽22遊記

amp 字符串 out 計算 add tree gcd wan har

Wannafly挑戰賽22遊記

幸福的人都是相似的,不幸的人各有各的不幸。
——題記

A-計數器

題目大意:

有一個計數器,計數器的初始值為\(0\),每次操作你可以把計數器的值加上\(a_1,a_2,\ldots,a_n\)中的任意一個整數,操作次數不限(可以為\(0\)次),問計數器的值對\(m\)取模後有幾種可能。

思路:

由裴蜀定理易得,答案即為\(\frac m{\gcd(m,a_1,a_2,\ldots,a_n)}\)

源代碼:

#include<cstdio>
#include<cctype>
#include<algorithm>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
const int N=101;
int a[N];
int main() {
    const int n=getint(),m=getint();
    int gcd=m;
    for(register int i=1;i<=n;i++) {
        a[i]=getint()%m;
        gcd=std::__gcd(gcd,a[i]);
    }
    printf("%d\n",m/gcd);
    return 0;
}

B-字符路徑

題目大意:

給一個含\(n\)個點\(m\)條邊的有向無環圖(允許重邊,點用\(1\)\(n\)的整數表示),每條邊上有一個字符,問圖上有幾條路徑滿足路徑上經過的邊上的字符組成的的字符串去掉空格後以大寫字母開頭,句號‘.‘結尾,中間都是小寫字母,小寫字母可以為\(0\)個。

思路:

拓撲序上DP,記錄每個點可以對應多少個大寫字母開頭的字符串,若在前面加上空格有多少種方案,在後面加上空格有多少種方案(反圖)。若當前邊為‘.‘則計算對答案的貢獻。

源代碼:

#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
inline int getch() {
    register char ch=getchar();
    while(!isalpha(ch)&&ch!='_'&&ch!='.') ch=getchar();
    return ch;
}
const int N=5e4+1;
struct Edge {
    int to;
    char w;
};
std::vector<Edge> e[N],e2[N];
inline void add_edge(const int &u,const int &v,const char &w) {
    e[u].push_back((Edge){v,w});
}
inline void add_edge2(const int &u,const int &v,const char &w) {
    e2[u].push_back((Edge){v,w});
}
int n,m,ind[N],outd[N];
unsigned upper[N],space[N],space2[N],ans;
std::queue<int> q;
void kahn2() {
    for(register int i=1;i<=n;i++) {
        if(outd[i]==0) q.push(i);
    }
    while(!q.empty()) {
        const int &x=q.front();
        for(auto &j:e2[x]) {
            const int &y=j.to;
            const char &w=j.w;
            if(w=='_') space2[y]+=space2[x]+1;
            if(!--outd[y]) q.push(y);
        }
        q.pop();
    }
}
void kahn() {
    for(register int i=1;i<=n;i++) {
        if(ind[i]==0) q.push(i);
    }
    while(!q.empty()) {
        const int &x=q.front();
        for(auto &j:e[x]) {
            const int &y=j.to;
            const char &w=j.w;
            if(isupper(w)) upper[y]+=space[x]+1;
            if(islower(w)) upper[y]+=upper[x];
            if(w=='_') {
                space[y]+=space[x]+1;
                upper[y]+=upper[x];
            }
            if(w=='.') ans+=upper[x]*(space2[y]+1);
            if(!--ind[y]) q.push(y);
        }
        q.pop();
    }
}
int main() {
    n=getint(),m=getint();
    for(register int i=0;i<m;i++) {
        const int u=getint(),v=getint();
        const char w=getch();
        add_edge(u,v,w);
        add_edge2(v,u,w);
        ind[v]++;
        outd[u]++;
    }
    kahn2();
    kahn();
    printf("%u\n",ans);
    return 0;
}

D-整數序列

題目大意:

給出一個長度為\(n\)的整數序列\(a_1,a_2,\ldots,a_n\),進行\(m\)次操作,操作分為兩類:

  1. 給出\(l,r,v\),將\(a_{l\sim r}\)分別加上\(v\)
  2. 給出\(l,r\),詢問\(\sum_{i=l}^r\sin(a_i)\)

思路:

根據三角恒等變換:
\[ \begin{align*} \sin(\alpha+\beta)=\sin\alpha\cdot\cos\beta+\cos\alpha\cdot\sin\beta\\cos(\alpha+\beta)=\cos\alpha\cdot\cos\beta-\sin\alpha\cdot\sin\beta \end{align*} \]

用線段樹維護區間\(\sum\sin(a_i)\)和區間\(\sum\cos(a_i)\)即可。

考慮寫成復數的形式,\(\cos\)作為實部,\(\sin\)作為虛部。使用std::complex可以很方便的維護。

源代碼:

#include<cmath>
#include<cstdio>
#include<cctype>
#include<complex>
#include<algorithm>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
typedef long long int64;
typedef std::complex<double> comp;
const int N=2e5+1;
class SegmentTree {
    #define _left <<1
    #define _right <<1|1
    #define mid ((b+e)>>1)
    private:
        comp val[N<<2],tag[N<<2];
        void push_up(const int &p) {
            val[p]=val[p _left]+val[p _right];
        }
        void push_down(const int &p) {
            if(tag[p]==comp(1,0)) return;
            val[p _left]*=tag[p];
            val[p _right]*=tag[p];
            tag[p _left]*=tag[p];
            tag[p _right]*=tag[p];
            tag[p]=comp(1,0);
        }
    public:
        void build(const int &p,const int &b,const int &e) {
            if(b==e) {
                const int x=getint();
                val[p]=comp(cos(x),sin(x));
                return;
            }
            tag[p]=comp(1,0);
            build(p _left,b,mid);
            build(p _right,mid+1,e);
            push_up(p);
        }
        void modify(const int &p,const int &b,const int &e,const int &l,const int &r,const comp &x) {
            if(b==l&&e==r) {
                tag[p]*=x;
                val[p]*=x;
                return;
            }
            push_down(p);
            if(l<=mid) modify(p _left,b,mid,l,std::min(mid,r),x);
            if(r>mid) modify(p _right,mid+1,e,std::max(mid+1,l),r,x);
            push_up(p);
        }
        double query(const int &p,const int &b,const int &e,const int &l,const int &r) {
            if(b==l&&e==r) return val[p].imag();
            push_down(p);
            double ret=0;
            if(l<=mid) ret+=query(p _left,b,mid,l,std::min(mid,r));
            if(r>mid) ret+=query(p _right,mid+1,e,std::max(mid+1,l),r);
            return ret;
        }
    #undef _left
    #undef _right
    #undef mid
};
SegmentTree t;
int main() {
    const int n=getint();
    t.build(1,1,n);
    const int m=getint();
    for(register int i=0;i<m;i++) {
        const int opt=getint(),l=getint(),r=getint();
        if(opt==1) {
            const int x=getint();
            t.modify(1,1,n,l,r,comp(cos(x),sin(x)));
        }
        if(opt==2) {
            printf("%.1f\n",t.query(1,1,n,l,r));
        }
    }
    return 0;
}

Wannafly挑戰賽22遊記