Educational Codeforces Round 38

Educational Codeforces Round 38 

C. Constructing Tests

假设已知\(n\)\(m\),那么我们构造出来的\(x\)应该等于\(n^2-\lfloor\frac n m\rfloor^2\)

那么现在已知\(x\),我们需要构造\((n+\lfloor\frac n m\rfloor)\cdot (n-\lfloor\frac n m\rfloor)=x\),考虑把\(x\)分解成两个数的乘积,即\(x=a\cdot b\),那么令\((n+\lfloor\frac n m\rfloor)=a,(n-\lfloor\frac n m\rfloor)=b\)

\(n = \frac{a+b}2,\lfloor \frac nm \rfloor = \frac {a-b}2\)

可以用整除分块来找出\(m\)

view code
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast,no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define endl "\n"
#define LL long long int
#define vi vector<int>
#define vl vector<LL>
#define all(V) V.begin(),V.end()
#define sci(x) scanf("%d",&x)
#define scl(x) scanf("%I64d",&x)
#define scs(s) scanf("%s",s)
#define pii pair<int,int>
#define pll pair<LL,LL>
#ifndef ONLINE_JUDGE
#define cout cerr
#endif
#define cmax(a,b) ((a) = (a) > (b) ? (a) : (b))
#define cmin(a,b) ((a) = (a) < (b) ? (a) : (b))
#define debug(x)  cerr << #x << " = " << x << endl
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
template <typename T> vector<T>& operator << (vector<T> &__container, T x){ __container.push_back(x); return __container; }
template <typename T> ostream& operator << (ostream &out, vector<T> &__container){ for(T _ : __container) out << _ << ' '; return out; }
const int MAXN = 2e5+7;

void solve(){
    int n; sci(n);
    if(n==0){
        cout << 1 << ' ' << 1 << endl;
        return;
    }
    for(int i = 1; i * i <= n; i++){
        if(n%i!=0) continue;
        if(i==n/i) continue;
        int x = n / i, y = i;
        if((x+y)&1) continue;
        int a = (x + y) >> 1;
        int b = (x - y) >> 1;
        for(int l = 1, r; l <= a; l = r + 1){
            r = a / (a / l);
            if(a / l == b){
                cout << a << ' ' << l << endl;
                return;
            }
        }
    }
    cout << -1 << endl;
    return;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("Local.in","r",stdin);
    freopen("ans.out","w",stdout);
    #endif
    int tt; for(sci(tt); tt--; solve());
    return 0;
}

D. Buy a Ticket

先把所有边权翻倍,那么就是考虑计算\(d[x]+a[x]\)的最小值了

定义\(dis[u]\)表示以\(u\)为起点的最小花费,初始化所有点的\(dis[u]=a[u]\),然后就能用类似\(dijkstra\)的做法来解决了,每次取花费最小的点,然后更新周围点的花费

view code
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast,no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define endl "\n"
#define LL long long int
#define vi vector<int>
#define vl vector<LL>
#define all(V) V.begin(),V.end()
#define sci(x) scanf("%d",&x)
#define scl(x) scanf("%I64d",&x)
#define scs(s) scanf("%s",s)
#define pii pair<int,int>
#define pll pair<LL,LL>
#define pil pair<int,LL>
#define pli pair<LL,int>
#ifndef ONLINE_JUDGE
#define cout cerr
#endif
#define cmax(a,b) ((a) = (a) > (b) ? (a) : (b))
#define cmin(a,b) ((a) = (a) < (b) ? (a) : (b))
#define debug(x)  cerr << #x << " = " << x << endl
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
template <typename T> vector<T>& operator << (vector<T> &__container, T x){ __container.push_back(x); return __container; }
template <typename T> ostream& operator << (ostream &out, vector<T> &__container){ for(T _ : __container) out << _ << ' '; return out; }
const int MAXN = 2e5+7;
int n, m;
LL w[MAXN];
vector<pil> G[MAXN];
void solve(){
    sci(n); sci(m);
    for(int i = 1; i <= m; i++){
        int u, v; LL wt;
        sci(u); sci(v); scl(wt);
        wt <<= 1;
        G[u] << pil(v,wt);
        G[v] << pil(u,wt);
    }
    for(int i = 1; i <= n; i++) scl(w[i]);
    priority_queue<pli,vector<pli>,greater<pli> > que;
    for(int i = 1; i <= n; i++) que.push(pli(w[i],i));
    while(!que.empty()){
        auto p = que.top(); que.pop();
        int u = p.second;
        if(w[u]!=p.first) continue;
        for(auto &e : G[u]){
            int v = e.first;
            LL ww = e.second;
            if(w[v]>w[u]+ww){
                w[v] = w[u] + ww;
                que.push(pli(w[v],v));
            }
        }
    }
    for(int i = 1; i <= n; i++) cout << w[i] << ' '; cout << endl;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("Local.in","r",stdin);
    freopen("ans.out","w",stdout);
    #endif
    solve();
    return 0;
}

E. Max History

首先对于这类题目,考虑每个值对答案的贡献,那么对于某个数,只要它前面的数都比他小且后面有比他大的数,那么它就能产生贡献

假设对于\(a_i\),有\(s\)个比它小的数

如果考虑枚举放在\(a_i\)前面的比它小的数有多少个

那么对于\(a_i\)的贡献应该就是\(\sum_{i=0}^s \tbinom si\cdot i! \cdot (n-i-1)! = s!\sum_{i=0}^s\frac{(n-i-1)!}{(s-i)!}\)

可以发现后面的部分可以卷积,然后我们就需要一个任意模数\(FFT\)了,就完了。

可是数据范围有\(10^6\),内存完全不够用

那么我们考虑把\(n-1-s\)个大于等于\(a_i\)的数排列一下,再把\(s\)个小于\(a_i\)的数排列一下,然后选出位置把\(s\)个小于\(a_i\)的数安置好,最后再把\(a_i\)放在第一个空位上,剩下的按顺序放在剩下的空位上即可

那么贡献应该是\(s!\cdot (n-1-s)!\cdot \tbinom{n}{s}\)

这个就不用卷了,直接算就好了

view code
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast,no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define endl "\n"
#define LL long long int
#define vi vector<int>
#define vl vector<LL>
#define all(V) V.begin(),V.end()
#define sci(x) scanf("%d",&x)
#define scl(x) scanf("%I64d",&x)
#define scs(s) scanf("%s",s)
#define pii pair<int,int>
#define pll pair<LL,LL>
#ifndef ONLINE_JUDGE
#define cout cerr
#endif
#define cmax(a,b) ((a) = (a) > (b) ? (a) : (b))
#define cmin(a,b) ((a) = (a) < (b) ? (a) : (b))
#define debug(x)  cerr << #x << " = " << x << endl
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
template <typename T> vector<T>& operator << (vector<T> &__container, T x){ __container.push_back(x); return __container; }
template <typename T> ostream& operator << (ostream &out, vector<T> &__container){ for(T _ : __container) out << _ << ' '; return out; }
const int MAXN = 1e6+7;
const int MOD = 1e9+7;
int fac[MAXN], inv[MAXN], rfac[MAXN], A[MAXN], cnt[MAXN];
int sum[MAXN];
int C(int n, int m){ return 1ll * fac[n] * rfac[m] % MOD * rfac[n-m] % MOD; }
void solve(){
    int n;
    sci(n);
    for(int i = 1; i <= n; i++) sci(A[i]);
    vi vec(n);
    for(int i = 0; i < n; i++) vec[i] = A[i+1];
    sort(all(vec)); vec.erase(unique(all(vec)),vec.end());
    for(int i = 1; i <= n; i++) A[i] = lower_bound(all(vec),A[i]) - vec.begin();
    for(int i = 1; i <= n; i++) cnt[A[i]]++;
    for(int i = 1; i <= n; i++) cnt[i] += cnt[i-1];
    int maxx = vec.size() - 1;
    int ret = 0;
    for(int i = 1; i <= n; i++){
        if(A[i]==maxx) continue;
        int s = cnt[A[i]-1];
        ret = (ret + 1ll * vec[A[i]] * fac[n-s-1] % MOD * fac[s] % MOD * C(n,s)) % MOD;
    }
    cout << ret << endl;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("Local.in","r",stdin);
    freopen("ans.out","w",stdout);
    #endif
    fac[0] = rfac[0] = inv[1] = 1;
    for(int i = 1; i < MAXN; i++) fac[i] = 1ll * fac[i-1] * i % MOD;
    for(int i = 2; i < MAXN; i++) inv[i] = 1ll * (MOD - MOD / i) * inv[MOD % i] % MOD;
    for(int i = 1; i < MAXN; i++) rfac[i] = 1ll * rfac[i-1] * inv[i] % MOD;
    solve();
    return 0;
}

F. Erasing Substrings

可以发现\(k\)次操作是相互独立的,也就是说顺序是没有关系的

因为操作序列只有\(\log n\)种,而且要求字典序最小,我们可以从前到后一个字符一个字符考虑,那么当我们考虑答案串第\(i\)个字符且删除操作使用的集合为\(msk\)的时候,这个字符的位置已经是确定的了,令\(dp[i][msk]\)表示考虑选择当前答案串第\(i\)个字符,且删除操作用了的集合为\(msk\)的情况下,能否作为字典序最小的答案串的第\(i\)个字符,这时候我们需要找到所有可行位置的最小字符即可,只有当前是最小字符才能转移到下一个位置上去

view code
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast,no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define endl "\n"
#define LL long long int
#define vi vector<int>
#define vl vector<LL>
#define all(V) V.begin(),V.end()
#define sci(x) scanf("%d",&x)
#define scl(x) scanf("%I64d",&x)
#define scs(s) scanf("%s",s)
#define pii pair<int,int>
#define pll pair<LL,LL>
#ifndef ONLINE_JUDGE
#define cout cerr
#endif
#define cmax(a,b) ((a) = (a) > (b) ? (a) : (b))
#define cmin(a,b) ((a) = (a) < (b) ? (a) : (b))
#define debug(x)  cerr << #x << " = " << x << endl
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
template <typename T> vector<T>& operator << (vector<T> &__container, T x){ __container.push_back(x); return __container; }
template <typename T> ostream& operator << (ostream &out, vector<T> &__container){ for(T _ : __container) out << _ << ' '; return out; }
const int MAXN = 5e3+7;
char s[MAXN];
int n;
bool f[MAXN];
void solve(){
    scs(s+1); n = strlen(s+1);
    int k = (int)log2(n), M = 1 << k;    
    f[0] = 1;
    for(int i = 1; i <= n - M + 1; i++){
        for(int msk = 0; msk < M; msk++) if(f[msk]) for(int bit = 0; bit < k; bit++) f[msk|(1<<bit)] = true;
        char best = 'z';
        for(int msk = 0; msk < M; msk++) if(f[msk]) cmin(best,s[msk+i]);
        for(int msk = 0; msk < M; msk++) f[msk] &= (s[msk+i]==best);
        cout << best;
    }
    cout << endl;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("Local.in","r",stdin);
    freopen("ans.out","w",stdout);
    #endif
    solve();
    return 0;
}

G. Shortest Path Queries

有道简单版的🔗

这道题增加了加边和删边,还有原本从\(1\)\(n\)的距离变成\(x\)\(y\),前者我们用线段树分治来解决,后者可以用\(dis_{ux}\bigoplus dis_{uy}\)来表示\(dis_{xy}\)

现在考虑那些环如何处理,可以用并查集来维护生成树,如果对于某一条边,两点已经联通了,那么就把这个环的异或和放到线性基里面去,否则把\(u\)\(v\)的合并到一起,对于每个点的距离,我们可以在并查集的过程中一并处理,具体做法就是在当前集合的根上打标记,询问的话就每次从当前点到根的路径上的所有标记的异或和

view code
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast,no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define endl "\n"
#define LL long long int
#define vi vector<int>
#define vl vector<LL>
#define all(V) V.begin(),V.end()
#define sci(x) scanf("%d",&x)
#define scl(x) scanf("%I64d",&x)
#define scs(s) scanf("%s",s)
#define pii pair<int,int>
#define pll pair<LL,LL>
#ifndef ONLINE_JUDGE
#define cout cerr
#endif
#define cmax(a,b) ((a) = (a) > (b) ? (a) : (b))
#define cmin(a,b) ((a) = (a) < (b) ? (a) : (b))
#define debug(x)  cerr << #x << " = " << x << endl
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
template <typename T> vector<T>& operator << (vector<T> &__container, T x){ __container.push_back(x); return __container; }
template <typename T> ostream& operator << (ostream &out, vector<T> &__container){ for(T _ : __container) out << _ << ' '; return out; }
const int MAXN = 2e5+7;
int n, m, q;
map<pii,pii> msk;
int fa[MAXN], dep[MAXN], dis[MAXN], base[31];
int ins(int x){
    for(int i = 30; ~i; i--){
        if(x>>i&1){
            if(!base[i]){
                base[i] = x;
                return i;
            }else x ^= base[i];
        }
    }
    return -1;
}
inline int findx(int x){ while(x!=fa[x]) x = fa[x]; return x; }
inline int getdis(int x){ int d = 0; while(x!=fa[x]) d ^= dis[x], x = fa[x]; return d; }
struct SegmentTree{
    int l[MAXN<<2], r[MAXN<<2];
    int head[MAXN<<2], nxt[MAXN<<4], tot;
    pair<pii,int> edge[MAXN<<4];
    #define ls(rt) rt << 1
    #define rs(rt) rt << 1 | 1
    void build(int L, int R, int rt = 1){
        l[rt] = L; r[rt] = R;
        head[rt] = -1;
        if(L + 1 == R) return;
        int mid = (L + R) >> 1;
        build(L,mid,ls(rt)); build(mid,R,rs(rt));
    }
    void modify(int L, int R, pair<pii,int> e, int rt = 1){
        if(L>=r[rt] or l[rt]>=R) return;
        if(L<=l[rt] and r[rt]<=R){
            nxt[tot] = head[rt];
            edge[tot] = e;
            head[rt] = tot++;
            return;
        }
        modify(L,R,e,ls(rt)); modify(L,R,e,rs(rt));
    }
    void dfs(int rt = 1){
        stack<int> basechange;                              // 线性基改变
        stack<pii> dsuchange;                               // 节点u, 父节点深度
        int x = 0, y = 0;
        for(int i = head[rt]; ~i; i = nxt[i]){
            auto &e = edge[i];
            int u = e.first.first, v = e.first.second, w = e.second;
            if(w==-1) x = u, y = v;
            else{
                int fu = findx(u), fv = findx(v);
                int du = getdis(u), dv = getdis(v);
                if(fu==fv){
                    int bs = ins(w^du^dv);
                    if(bs!=-1) basechange.push(bs);
                }else{
                    if(dep[fu]==dep[fv]){
                        dsuchange.push(pii(fu,dep[fv]));
                        fa[fu] = fv; dep[fv]++; dis[fu] = du^dv^w;
                    }else{
                        if(dep[fu]<dep[fv]) swap(fu,fv);
                        dsuchange.push(pii(fv,dep[fu]));
                        fa[fv] = fu; dis[fv] = du^dv^w;
                    }
                }
            }
        }
        if(l[rt]+1!=r[rt]) dfs(ls(rt)), dfs(rs(rt));
        if(x!=0){
            int d = getdis(x) ^ getdis(y);
            for(int i = 30; ~i; i--) if(d>>i&1) d ^= base[i];
            cout << d << endl;
        }
        while(!basechange.empty()) base[basechange.top()] = 0, basechange.pop();
        while(!dsuchange.empty()){
            int u = dsuchange.top().first;
            dep[fa[u]] = dsuchange.top().second;
            fa[u] = u;
            dis[u] = 0;
            dsuchange.pop();
        }
    }
}ST;
void solve(){
    sci(n); sci(m);
    for(int i = 1; i <= m; i++){
        int u, v, w;
        sci(u); sci(v); sci(w);
        if(u>v) swap(u,v);
        msk[pii(u,v)] = pii(0,w);
    }
    sci(q);
    ST.build(0,q+1);
    for(int i = 1; i <= q; i++){
        int tp, u, v, w; sci(tp);
        if(tp==1){
            sci(u); sci(v); sci(w);
            if(u>v) swap(u,v);
            msk[pii(u,v)] = pii(i,w);
        }else if(tp==2){
            sci(u); sci(v);
            if(u>v) swap(u,v);
            ST.modify(msk[pii(u,v)].first,i,make_pair(pii(u,v),msk[pii(u,v)].second));
            msk.erase(pii(u,v));
        }else{
            sci(u); sci(v);
            ST.modify(i,i+1,make_pair(pii(u,v),-1));
        }
    }
    for(auto p : msk) ST.modify(p.second.first,q+1,make_pair(p.first,p.second.second));
    for(int i = 1; i <= n; i++) fa[i] = i;
    ST.dfs();
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("Local.in","r",stdin);
    freopen("ans.out","w",stdout);
    #endif
    solve();
    return 0;
}
posted @ 2020-09-02 17:33  _kiko  阅读(88)  评论(0编辑  收藏