XIX Open Cup, Grand Prix of SPb【雜題】


啊哈,好像學會開火車了

感覺單人 vp ACM 有點自閉,但是沒人陪我訓練所以沒辦法了

G Least Number

給定正整數 \(n\) 和數字 \(d\),求最小的數字和為 \(n\) 且不含數字 \(d\) 的正整數。

\(2\le n\le 10^6\)


垃圾分類討論題,隨便測一測就寫完了(

#include<bits/stdc++.h>
using namespace std;
int n, d;
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> d;
	if(d == 9){
		if(n & 7) putchar((n & 7) | '0');
		for(int i = n>>3;i;-- i) putchar('8');
	} else if(!d || n % 9 != d){
		if(n % 9) putchar(n % 9 + '0');
		for(int i = n/9;i;-- i) putchar('9');
	} else if(d == 8){
		putchar('1'); putchar('7');
		for(int i = n/9;i;-- i) putchar('9');
	} else if(n > 9){
		putchar(d + '1'); putchar('8');
		for(int i = n/9;i > 1;-- i) putchar('9');
	} else printf("1%d\n", n-1);
}

F Dominating Subarray

對於兩個長為 \(k\) 的正整數列 \(b,c\),稱 \(b\) 偏序 \(c\) 當且僅當 \(b_i\ge c_i\)

給定長為 \(n\) 的正整數列 \(a\) 和正整數 \(k\),求一個偏序所有長為 \(k\) 的子段的長為 \(k\) 的子段。

\(k\le n\le 10^5\)\(a_i\le 10^6\)


\(k=n\) 時顯然有解,否則還是分類討論:

  • 若是前綴,則 \(a_i\le a_{\min(k,i-1)}\pod{i\ge 2}\)
  • 若是后綴,則 \(a_i\le a_{\max(n-k,i)+1}\pod{i<n}\)
  • 否則,必須是連續 \(k\) 個最大值。

直接判即可,時間復雜度 \(O(n)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 100003;
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;} 
int n, k, a[N], mx;
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> k;
	if(k == n){putchar('1'); return 0;}
	for(int i = 1;i <= n;++ i){cin >> a[i]; chmax(mx, a[i]);}
	bool flg = true;
	for(int i = 2;i <= n && flg;++ i) flg &= a[i] <= a[min(k,i-1)];
	if(flg){putchar('1'); return 0;}
	flg = true;
	for(int i = n-1;i && flg;-- i) flg &= a[i] <= a[max(n-k,i)+1];
	if(flg){printf("%d", n-k+1); return 0;}
	for(int i = 2, j = 0;i < n;++ i)
		if(a[i] == mx){
			if(++j == k){printf("%d", i-k+1); return 0;}
		} else j = 0;
	puts("-1");
}

D Distance in Crosses

【題目描述略】

顯然十字也構成四連通網格,所以也是算曼哈頓距離。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL xa, ya, xb, yb;
LL div5(LL x){return x >= 0 ? (x+2) / 5 : (x-2) / 5;}
int main(){
	ios::sync_with_stdio(false);
	cin >> xa >> ya >> xb >> yb;
	LL x1 = div5(2*xa+ya), y1 = div5(xa-2*ya), x2 = div5(2*xb+yb), y2 = div5(xb-2*yb);
	printf("%lld\n", llabs(x1-x2) + llabs(y1-y2));
}

I Multiplication

這是一道交互題

交互庫有一個奇數 \(x\in[1,2^{31}-1]\),給定正偶數 \(n\),你需要給出 \(n\)\(<2^{31}\) 的不同自然數 \(a_i\),交互庫會隨機抽取其中的 \(n/2\)\(a_i\),將 \(b_i=a_ix\bmod 2^{31}\) 打亂之后告訴你,求 \(x\)

\(4\le n\le 10^5\)


你給出的 \(a_i\) 直接取隨機奇數即可。

首先看 \(b_1\),有 \(x^{-1}=a_{?}\cdot b_1^{-1}\),將可能的 \(n\)\(x^{-1}\) 算出來,然后用 \(\forall i,b_i\cdot x^{-1}=a_{?}\) 大力判斷。期望復雜度 \(O(n)\)

不要貪圖僥幸直接隨機,必須用個 set 去重。

而且這個模 \(2^{31}\) 好陰間啊,能不能直接模 \(2^{32}\) 爽快點(

#include<bits/stdc++.h>
#define PB emplace_back
using namespace std;
const int N = 100003;
const unsigned ALL = -1u>>1;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
int n, m, pos[N];
unsigned b[N], ans;
set<unsigned> S;
unsigned Inv(unsigned x){
	unsigned res = 1;
	for(int i = 1;i < 31;++ i)
		if(res*x>>i&1) res |= 1u<<i;
	return res;
}
vector<unsigned> res;
int main(){
	ios::sync_with_stdio(false);
	cin >> n; m = n>>1;
	while(S.size() < n) S.insert((rng()&ALL)|1);
	for(unsigned u : S) cout << u << ' ';
	cout << endl;
	for(int i = 0;i < m;++ i)
		cin >> b[i];
	unsigned tmp = Inv(b[0]);
	for(unsigned u : S) res.PB(u*tmp&ALL);
	for(int i = 1;i < m && res.size() > 1;++ i){
		vector<unsigned> nxt;
		for(unsigned x : res){
			unsigned tmp = b[i]*x&ALL;
			if(S.count(tmp)) nxt.PB(x);
		} res.swap(nxt);
	}
	cout << Inv(res[0]) << endl;
}

A Create the Best Pet

【題目描述略】

也就只能大力枚舉判斷了,因為用 Gene 判比較快所以先用 Gene 判再用 Power 判,跑個幾秒就跑出來了。

#include<bits/stdc++.h>
using namespace std;
const unsigned ALL = -1u>>1;
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
struct u31 {
	unsigned x;
	u31(unsigned a = 0): x(a&ALL){}
	u31 operator + (const u31 &o) const {return x + o.x & ALL;}
	u31 operator * (const u31 &o) const {return x * o.x & ALL;}
	u31 Inv() const {
		unsigned res = 1;
		for(int i = 1;i < 31;++ i)
			if(res*x>>i&1) res ^= 1u<<i;
		return res;
	}
};
u31 calc(const string &s){
	int len = s.size(); u31 res;
	for(int i = 0;i < len;++ i)
		res = res * 31 + s[i];
	return res;
}
int mx, ans[8], info[8];
unsigned pos;
int main(){
	unsigned L, R; string str;
	ios::sync_with_stdio(false);
	cin >> L >> R >> str; R += L;
	unsigned _ = /*Magic Number*/^calc(str).x;
	for(unsigned i = L;i <= R;++ i){
		u31 sd(_ ^ i);
		int scr = 0, tmp[8];
		for(int i = 0;i < 8;++ i){
			tmp[i] = 0;
			for(int j = 0;j < 10000;++ j){
				sd = sd * 1234567893 + 151515151;
				tmp[i] += sd.x % 3 - 1;
			}
			scr += abs(tmp[i]);
		}
		if(chmax(mx, scr)){
			pos = i;
			for(int i = 0;i < 8;++ i) ans[i] = tmp[i] + 500;
			for(int i = 0;i < 8;++ i){
				sd = sd * 1234567893 + 151515151;
				info[i] = sd.x % 5;
			}
		}
	}
	printf("%u %.3f\n", pos, mx / 8.);
	for(int i = 0;i < 8;++ i) printf("%d ", ans[i]);
	putchar('\n');
	for(int i = 0;i < 8;++ i) printf("%d ", info[i]);
}

J Guess Two Strings

這是個 jb 的交互題

出題人有兩個長為 \(n\)\(\texttt{01}\) 字符串 \(S,T\),他按如下方式生成 \(q\)\(\texttt{01}\) 字符串並告訴你,求 \(S,T\)

  • 隨機選 \(S\)\(T\) 之一,翻轉隨機的 \(k\) 個 bit。

\(n=q=100\)\(k=15\)\(S\le T\)


這種東西顯然直接瞎搞搞就行了。

輸入的 \(q\) 個串形成兩個團。隨機枚舉其中兩個串,假裝它們屬於不同的團,按其他串到它們的漢明距離划分成兩個團。求 \(S,T\) 的時候直接對每一位取眾數即可。期望復雜度 \(O(nq)\)

#include<bits/stdc++.h>
using namespace std;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); 
int n, k, q;
string S[100], ans[2];
bool a[100];
int dis(const string &a, const string &b){
	int res = 0;
	for(int i = 0;i < n;++ i) res += a[i] != b[i];
	return res;
}
bool work(){
	int p1 = rng() % q, p2 = rng() % q;
	if(p1 == p2) return true;
	for(int i = 0;i < q;++ i)
		a[i] = dis(S[i], S[p1]) > dis(S[i], S[p2]);
	for(int _ = 0;_ < 2;++ _)
		for(int i = 0;i < n;++ i){
			int cnt[2] = {};
			for(int j = 0;j < q;++ j)
				if(a[j] == _) ++ cnt[S[j][i] ^ '0'];
			ans[_][i] = (cnt[0] < cnt[1]) | '0';
		}
	for(int i = 0;i < q;++ i)
		if(dis(S[i], ans[0]) > k && dis(S[i], ans[1]) > k)
			return true;
	if(ans[0] > ans[1]) swap(ans[0], ans[1]);
	return false;
}
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> k >> q;
	for(int i = 0;i < q;++ i){
		cout << '?' << endl;
		cin >> S[i];
	}
	ans[0].resize(n);
	ans[1].resize(n);
	while(work());
	cout << "! " << ans[0] << ' ' << ans[1] << endl;
}

K Beautiful Tables

【題目描述略】

顯然題目條件是每行每列形成等差數列,隨便手玩一下就知道等價於存在四個數 \(a,b,c,d\),使得 \(a_{i,j}=a+bi+cj+dij\),直接解線性方程組即可。

最后蜜汁 WA 沒調出來,賽后仔細看看才發現是分母算出負數了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, m, cnt;
char str[10];
LL gcd(LL a, LL b){return b ? gcd(b, a % b) : a;}
struct Frac {
	LL a, b;
	Frac(LL x = 0, LL y = 1){LL g = gcd(x, y); a = x / g; b = y / g; if(b < 0){a = -a; b = -b;}}
	Frac operator + (const Frac &o) const {
		LL g = gcd(b, o.b);
		return Frac(o.b / g * a + b / g * o.a, b / g * o.b);
	}
	Frac operator - (const Frac &o) const {
		LL g = gcd(b, o.b);
		return Frac(o.b / g * a - b / g * o.a, b / g * o.b);
	}
	Frac operator * (const Frac &o) const {
		LL g1 = gcd(b, o.a), g2 = gcd(a, o.b);
		return Frac(a / g2 * (o.a / g1), b / g1 * (o.b / g2));
	}
	Frac Inv() const {return Frac(b, a);}
	Frac operator / (const Frac &o) const {return this->operator*(o.Inv());}
	operator bool() const {return a;}
	friend ostream& operator << (ostream& out, const Frac &o){
		out << o.a << '/' << o.b; return out;
	}
} mat[4][5], tmp[5];
void Ins(Frac *a){
	for(int i = 0;i < 4;++ i) if(a[i]){
		if(!mat[i][i]){
			for(int j = i+1;j < 5;++ j) mat[i][j] = a[j] / a[i];
			mat[i][i] = 1; ++cnt; return;
		}
		for(int j = i+1;j < 5;++ j) a[j] = a[j] - a[i] * mat[i][j];
		a[i] = 0;
	}
	if(a[4]){cout << "None" << endl; exit(0);}
}
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> m;
	if(n == 1){tmp[1] = 1; tmp[0] = tmp[2] = tmp[3] = tmp[4] = 0; Ins(tmp);}
	if(m == 1){tmp[2] = 1; tmp[0] = tmp[1] = tmp[3] = tmp[4] = 0; Ins(tmp);}
	if(n == 1 || m == 1){tmp[3] = 1; tmp[0] = tmp[1] = tmp[2] = tmp[4] = 0; Ins(tmp);}
	for(int i = 0;i < n;++ i)
		for(int j = 0;j < m;++ j){
			cin >> str;
			if(str[0] != '?'){
				tmp[0] = 1; tmp[1] = i;
				tmp[2] = j; tmp[3] = i*j;
				tmp[4] = atoi(str); Ins(tmp);
			}
		}
	if(cnt == 4){
		for(int i = 3;~i;-- i){
			tmp[i] = mat[i][4];
			for(int j = i+1;j < 4;++ j)
				tmp[i] = tmp[i] - mat[i][j] * tmp[j];
		}
		cout << "Unique" << endl;
		for(int i = 0;i < n;++ i){
			for(int j = 0;j < m;++ j)
				cout << tmp[0]+tmp[1]*Frac(i)+tmp[2]*Frac(j)+tmp[3]*Frac(i*j) << ' ';
			cout << endl;
		}
		return 0;
	}
	for(int i = 3;~i;-- i){
		tmp[i] = mat[i][4];
		for(int j = i+1;j < 4;++ j)
			tmp[i] = tmp[i] - mat[i][j] * tmp[j];
	}
	cout << "Multiple" << endl;
	for(int i = 0;i < n;++ i){
		for(int j = 0;j < m;++ j)
			cout << tmp[0]+tmp[1]*Frac(i)+tmp[2]*Frac(j)+tmp[3]*Frac(i*j) << ' ';
		cout << endl;
	}
	cout << "and" << endl;
	for(int i = 3;~i;-- i){
		tmp[i] = mat[i][i] ? mat[i][4] : Frac(1);
		for(int j = i+1;j < 4;++ j)
			tmp[i] = tmp[i] - mat[i][j] * tmp[j];
	}
	for(int i = 0;i < n;++ i){
		for(int j = 0;j < m;++ j)
			cout << tmp[0]+tmp[1]*Frac(i)+tmp[2]*Frac(j)+tmp[3]*Frac(i*j) << ' ';
		cout << endl;
	}
}

H Galactic Governments

給定 \([0,C]^k\)\(n\) 個子長方體,求水平序最小的不在任何一個長方體內的格子。

\(n\le 18\)\(k\le 10\)\(C\le 1000\)


二分之后轉化為算一個子長方體與這 \(n\) 個子長方體的並的交,直接容斥,時間復雜度 \(O(2^nk\log C)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1073740847;
int n, k, C, a[18][10], b[18][10], L[10], R[10];
void qmo(int &x){x += x >> 31 & mod;}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int dfs(int d){
    for(int i = 0;i < k;++ i) if(L[i] >= R[i]) return 0;
    if(d == n){
        int ans = 1;
        for(int i = 0;i < k;++ i) ans = (LL)ans * (R[i]-L[i]) % mod;
        return ans;
    }
    int tl[10], tr[10], ans = dfs(d+1);
    memcpy(tl, L, sizeof L); memcpy(tr, R, sizeof R);
    for(int i = 0;i < k;++ i){
        chmax(L[i], a[d][i]); chmin(R[i], b[d][i]);
    }
    qmo(ans -= dfs(d+1));
    memcpy(L, tl, sizeof L); memcpy(R, tr, sizeof R);
    return ans;
}
int main(){
    ios::sync_with_stdio(false);
    cin >> n >> k >> C;
    for(int i = 0;i < k;++ i) R[i] = C;
    for(int i = 0;i < n;++ i){
        for(int j = 0;j < k;++ j) cin >> a[i][j];
        for(int j = 0;j < k;++ j) cin >> b[i][j];
    }
    if(!dfs(0)){puts("NO"); return 0;}
    puts("YES");
    for(int i = 0;i < k;++ i){
        while(L[i] + 1 < R[i]){
            int tmp = R[i]; R[i] = L[i] + R[i] >> 1;
            if(!dfs(0)){L[i] = R[i]; R[i] = tmp;}
        }
        printf("%d ", L[i]);
    }
}

C Clique Festival

給定 \(n\) 個點的無向連通圖,其邊集是 \(k\) 個邊權相同的團 \(C_i\),求兩兩最短路長度之和。

\(n\le 10^5\)\(k\le 18\)\(1\le w\le 10^7\)\(\sum |C_i|\le 3\cdot 10^5\)


考慮兩個點 \(u,v\) 之間的最短路,因為團比較少所以轉化為團之間的最短路。

建有向圖 \(G\) 表示若 \(C_i\cap C_j\ne\varnothing\) 則連邊 \(i\to j\),邊權為 \(a_j\),設 \(u,v\) 分別屬於 \(C_i,C_j\),則 \(\text{dis}(u,v)=\min_{i,j}\{\text{dis}_G(i,j)+a_i\}\)

\(\text{dis}_G(i,j)\) 顯然很容易算。考慮按權值從小到大枚舉 \((i,j)\),數出對應的 \(u,v\) 數量。設 \(D_i\) 表示所有處理過的 \((i,j)\)\(j\) 的集合。

枚舉 \(u\)\(v\) 的取值是 \(C_j\backslash\bigcup\limits_{C_a\supseteq u,b\in D_a}C_b\),這個是可以 \(O(k2^k)\) 預處理 \(O(k)\) 計算的,具體不太好描述就不寫了

總時間復雜度 \(O(k^2\sum |C_i|+k2^k+nk^2/w)\),好像吊打標算了,改天出到模擬賽里

#include<bits/stdc++.h>
#define PB emplace_back
#define fi first
#define se second
#define ALL(x) x.begin(), x.end()
using namespace std;
typedef pair<int, int> pii;
typedef long long LL;
const int N = 100003, M = 1<<18;
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, K, lim, a[18], G[18][18], msk[N], b[M], d[18];
LL ans;
bitset<N> C[18];
vector<pii> vec;
vector<int> VC[18];
int main(){
    ios::sync_with_stdio(false);
    cin >> n >> K; lim = 1<<K;
    for(int i = 0, m;i < K;++ i){
        cin >> a[i] >> m; VC[i].resize(m);
        for(int j = 0;j < m;++ j){
            cin >> VC[i][j];
            msk[--VC[i][j]] |= 1<<i;
            C[i].set(VC[i][j]);
        }
    }
    for(int i = 0;i < n;++ i) ++ b[msk[i]];
    for(int S = 1;S < lim;++ S){
        int res = 1e9;
        for(int u = 0;u < K;++ u) if(S>>u&1) chmin(res, a[u]);
        ans -= (LL)res*b[S];
    }
    for(int i = 1;i < lim;i <<= 1)
        for(int j = 0;j < lim;j += i<<1)
            for(int k = 0;k < i;++ k)
                b[i+j+k] += b[j+k];
    for(int i = 0;i < K;++ i)
        for(int j = 0;j < K;++ j)
            G[i][j] = i == j ? 0 : ((C[i] & C[j]).any() ? a[j] : 1e9);
    for(int k = 0;k < K;++ k)
        for(int i = 0;i < K;++ i)
            for(int j = 0;j < K;++ j)
                chmin(G[i][j], G[i][k] + G[k][j]);
    for(int i = 0;i < K;++ i)
        for(int j = 0;j < K;++ j){
            G[i][j] += a[i]; vec.PB(i, j);
        }
    sort(ALL(vec), [&](const pii &a, const pii &b){return G[a.fi][a.se] < G[b.fi][b.se];});
    for(pii _ : vec){
        int ca = _.fi, cb = _.se;
        LL res = 0;
        for(int u : VC[ca]){
            int tmp = 0;
            for(int i = 0;i < K;++ i)
                if(msk[u]>>i&1) tmp |= d[i];
            tmp ^= lim-1; res += b[tmp] - b[tmp&~(1<<cb)];
        } d[ca] |= 1<<cb; ans += res*G[ca][cb];
    }
    printf("%lld\n", ans>>1);
}

B Graph and Machine

給定 \(k\) 個點 \(m\) 條邊的無向簡單連通圖 \((V,E)\),每個點 \(u\) 有點權 \(c_u\in\{0,1\}\)

給定 \(N\) 個點的 DAG,點 \(s\) 沒有入邊,稱為源點,點 \(t_0,t_1\) 沒有出邊,稱為匯點,匯點之外的點 \(u\) 有標記 \(l_u\in\{1,2,\cdots,m\}\),且有兩條出邊分別連向 \(o_{u,0}\)\(o_{u,1}\)

判斷是否 \(\forall (x_1,x_2,\cdots,x_m)\in\{0,1\}^m\),在 DAG 上從 \(s\) 出發,每次從 \(u\) 走到 \(o_{u,c_{l_u}}\),最終走到 \(t_1\) 當且僅當 \(\forall u\in V,\bigoplus\limits_{(u,v)\in E} x_u=c_v\)

\(N,m\le 3\cdot 10^5\)\(N\ge 3\)對於所有從源點到匯點的路徑,經過的點的標記不同

【未完待續】


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM