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;
}