目錄
1001 ^&^
1002 array
1003 K-th occurrence
1004 path
1005 huntian oy
1006 Shuffle Card
1007 Windows Of CCPC
1008 Fishing Master
1009 Kaguya
1010 Touma Kazusa's function
1011 sakura
1001 ^&^
首先貪心,能不加就不加,只有兩個都是1的時候才需要C。
可能最后導致C是0,這樣要或上兩個lowbit里面比較小的那個。
比如:
1011010
0100100
這個的話,假如或上1,就破壞了結果的最小性(結果從0變成了1)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int T;
scanf("%d", &T);
while(T--) {
unsigned int A, B;
scanf("%u%u", &A, &B);
unsigned int C = 0;
for(int i = 31; i >= 0; --i) {
unsigned int bitmask = 1u << i;
if((A & bitmask) && (B & bitmask))
C |= bitmask;
}
if(C == 0)
C |= min((A & -A), (B & -B));
printf("%u\n", C);
}
return 0;
}
事實上就是直接&起來???
1002 array
xy說,反序建樹,不在前面出現那就要么在后面出現,要么在前面被刪除的集合里面出現。
貌似很有道理,因為題目問的是,[1,r]區間內不出現的第一個>=k的數,既然不在[1,r]里面出現就一定,要么在[r+1,n]里面出現,要么在[1,r]里面出現但是被刪除了。
注意到要是反過來建立主席樹的話,刪除一個數就不需要改動主席樹里的任何東西。
因為,要在[r+1,n]中尋找大於等於k的第一個數或者在被刪除的集合中尋找大於等於k的第一個數,而這兩個集合的並集恰好就是除去[1,r]中剩余的所有元素的集合。
那么根據這個思路用兩個簡單操作湊出一個靜態主席樹,常數略大。
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
namespace FastIO {
#define BUF_SIZE 1000000
bool IOError = 0;
inline char NextChar() {
static char buf[BUF_SIZE], *pl = buf + BUF_SIZE, *pr = buf + BUF_SIZE;
if(pl == pr) {
pl = buf, pr = buf + fread(buf, 1, BUF_SIZE, stdin);
if(pr == pl) {
IOError = 1;
return -1;
}
}
return *pl++;
}
#undef BUF_SIZE
inline bool Blank(char c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\t';
}
template<class T> inline void Read(T &x) {
char c;
while(Blank(c = NextChar()));
if(!IOError) {
for(x = 0; '0' <= c && c <= '9'; c = NextChar())
x = (x << 3) + (x << 1) + c - '0';
}
}
template<class T> inline void Read2(T &x) {
char c;
bool f = 0;
while(Blank(c = NextChar()));
if(!IOError) {
if(c == '-') {
f = 1;
c = NextChar();
}
for(x = 0; '0' <= c && c <= '9'; c = NextChar())
x = (x << 3) + (x << 1) + c - '0';
}
if(f)
x = -x;
}
template<class T> inline void PutChar(T x) {
if(x > 9)
PutChar(x / 10);
putchar(x % 10 + '0');
}
template<class T> inline void Write(T &x) {
PutChar(x);
putchar('\n');
}
template<class T> inline void Write2(T &x) {
if(x<0){
putchar('-');
PutChar(-x);
}
else
PutChar(x);
putchar('\n');
}
}
using namespace FastIO;
const int MAXN = 100000 + 5;
int T[MAXN], tcnt;
int cnt[MAXN * 20], L[MAXN * 20], R[MAXN * 20];
int a[MAXN];
inline void init() {
tcnt = 0;
}
inline int build(int l, int r) {
int rt = ++tcnt;
cnt[rt] = 0;
if(l < r) {
L[rt] = build(l, mid);
R[rt] = build(mid + 1, r);
}
return rt;
}
inline int update(int pre, int l, int r, int x) {
int rt = ++tcnt;
R[rt] = R[pre];
L[rt] = L[pre];
cnt[rt] = cnt[pre] + 1;
if(l < r) {
if(x <= mid)
L[rt] = update(L[pre], l, mid, x);
else
R[rt] = update(R[pre], mid + 1, r, x);
}
return rt;
}
const int INF = 1e9;
//查詢[u+1,v]中排名為rk的數
inline int query1(int u, int v, int l, int r, int rk) {
//比整個區間的數都多,那是INF
if(rk > cnt[v] - cnt[u])
return INF;
//直到進入一個葉子
while(l < r) {
int tmp = cnt[L[v]] - cnt[L[u]];
if(tmp < rk) {
//整個左子樹加起來的數量都是<rk,所以這個排名在右子樹中
rk -= tmp;
u = R[u], v = R[v], l = mid + 1;
} else {
u = L[u], v = L[v], r = mid;
}
}
//最后到達了一個葉子
return l;
}
//查詢[u+1,v]中值不超過va的數的個數
inline int query2(int u, int v, int l, int r, int va) {
int res = 0;
while(l < r && va < r) {
if(mid <= va) {
//整個左子樹都是<=va
res += cnt[L[v]] - cnt[L[u]];
u = R[u], v = R[v], l = mid + 1;
} else
u = L[u], v = L[v], r = mid;
}
if(va >= l)
res += cnt[v] - cnt[u];
return res;
}
int n;
//[1,r]里面出現的第一個>=k的數,INF表示不存在
inline int Query(int r, int k) {
int rk = query2(T[1 - 1], T[r], 1, n, k - 1);
return query1(T[1 - 1], T[r], 1, n, rk + 1);
}
set<int> s;
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int Ti;
Read(Ti);
while(Ti--) {
int m;
Read(n),Read(m);
for(int i = 1; i <= n; ++i)
Read(a[i]);
//反向建樹
init();
T[0] = build(1, n);
for(int i = n, j = 1; i >= 1; --i, ++j)
T[j] = update(T[j - 1], 1, n, a[i]);
s.clear();
s.insert(n + 1);
int LastAns = 0;
int op, pos, r, k;
while(m--) {
Read(op);
if(op == 1) {
Read(pos);
pos ^= LastAns;
s.insert(a[pos]);
} else {
Read(r),Read(k);
r ^= LastAns;
k ^= LastAns;
LastAns = Query(n - r, k);
LastAns = min(LastAns, *s.lower_bound(k));
Write(LastAns);
}
}
}
return 0;
}
所以說干嘛要反序建樹?
1004 path
直接暴力找前k短。假如是強行bfs插入的話會被爆空間。這個時候考慮一下問題的特性,其實是要前k短。那么對每個邊排序,每個點插入最小的邊。當這個邊被彈出的時候再把兄弟邊和后繼點的最小邊插進來,因為兄弟邊和后繼點的最小邊肯定不會比當前邊更好所以枚舉順序正確。而這樣枚舉的確也不會遺漏任何一個情況,不像那種根據長度暴力剪枝的做法,因為每個路徑必定有一個開始的邊,然后經過一系列的后繼邊,這樣保證只要他足夠小就會被算法枚舉到。
單組復雜度 \(O(n+m*logm+q+maxk*log(n+maxk))\) ,按題目的規模近似 \(O(nlogn)\) 。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 50000 + 5;
struct Edge {
int v, w;
bool operator<(const Edge& e)const {
return w < e.w;
}
} e;
vector<Edge> G[MAXN];
struct Path {
ll len;
int u, v;
int id;
bool operator<(const Path& p)const {
return len > p.len;
}
} p, tmp;
priority_queue<Path> pq;
void Init(int n) {
for(int i = 1; i <= n; ++i)
G[i].clear();
while(!pq.empty())
pq.pop();
}
int query[MAXN];
ll ans[MAXN];
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int T;
scanf("%d", &T);
while(T--) {
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
Init(n);
while(m--) {
int u;
scanf("%d%d%d", &u, &e.v, &e.w);
G[u].push_back(e);
}
for(int i = 1; i <= n; ++i) {
if(G[i].size()) {
sort(G[i].begin(), G[i].end());
p.len = G[i][0].w;
p.u = i;
p.v = G[i][0].v;
p.id = 0;
pq.push(p);
}
}
int maxquery = 0;
for(int i = 1; i <= q; ++i) {
scanf("%d", &query[i]);
maxquery = max(maxquery, query[i]);
}
for(int i = 1; i <= maxquery; ++i) {
p = pq.top();
pq.pop();
ans[i] = p.len;
ll len = p.len;
int u = p.u;
int id = p.id;
if(id + 1 < G[u].size()) {
tmp.len = len - G[u][id].w + G[u][id + 1].w;
tmp.u = u;
tmp.v = G[u][id + 1].v;
tmp.id = id + 1;
pq.push(tmp);
}
int v = p.v;
if(G[v].size()) {
tmp.len = len + G[v][0].w;
tmp.u = v;
tmp.v = G[v][0].v;
tmp.id = 0;
pq.push(tmp);
}
}
for(int i = 1; i <= q; ++i) {
printf("%lld\n", ans[query[i]]);
}
}
return 0;
}
1005 huntian oy
zf知道后面那串東西當a,b互質的時候就是i-j,那么變成求 \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i}(i-j)[gcd(i,j)==1]\) 。
要求:
\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i}(i-j)[gcd(i,j)==1]\)
很明顯把i提前:
\(\sum\limits_{i=1}^{n}i\varphi(i)\space - \space \sum\limits_{j=1}^{i}j[gcd(i,j)==1]\)
后面那個是求:
\(\sum\limits_{i=1}^{n}i[gcd(i,n)==1]\)
顯然:
\(\sum\limits_{i=1}^{n}i[gcd(i,n)==1] = \frac{n}{2}([n==1]+\varphi(n))\)
代進去得到:
\(\sum\limits_{i=1}^{n}i\varphi(i) - \frac{i}{2}([i==1]+\varphi(i))\)
即:
\(-\frac{1}{2}+\frac{1}{2}\sum\limits_{i=1}^{n}i\varphi(i)\)
這個是卷個g=id就可以得到了。
寫得太豪放了差點T了,收斂一點的話還是很穩的。
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int mod = 1e9 + 7;
int qpow(ll x, int n) {
ll res = 1;
while(n) {
if(n & 1)
res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
const int inv2 = (mod + 1) >> 1;
const int inv6 = qpow(6, mod - 2);
const int MAXN = pow(1e9, 2.0 / 3.0);
int pri[MAXN + 10], pritop;
int pk[MAXN + 10];
ll sum[MAXN + 10];
void sieve(int n = MAXN) {
sum[1] = 1;
pk[1] = 1;
for(int i = 2; i <= n; i++) {
if(!pk[i]) {
pri[++pritop] = i;
pk[i] = i;
sum[i] = 1ll * i * (i - 1);
if(sum[i] >= mod)
sum[i] %= mod;
}
for(int j = 1, t; j <= pritop && (t = i * pri[j]) <= n; j++) {
if(i % pri[j]) {
pk[t] = pk[pri[j]];
sum[t] = sum[i] * sum[pri[j]] ;
if(sum[t] >= mod)
sum[t] %= mod;
} else {
pk[t] = pri[j] * pk[i];
if(t == pk[t]) {
sum[t] = 1ll * t * (t - t / pri[j]) ;
if(sum[t] >= mod)
sum[t] %= mod;
} else {
sum[t] = sum[pk[t]] * sum[t / pk[t]] ;
if(sum[t] >= mod)
sum[t] %= mod;
}
break;
}
}
}
for(int i = 2; i <= n; i++) {
sum[i] += sum[i - 1];
if(sum[i] >= mod)
sum[i] -= mod;
}
}
inline int s1(int n) {
ll t = (1ll * n * (n + 1)) >> 1;
if(t >= mod)
t %= mod;
return t;
}
inline int s2(ll n) {
ll t = n * (n + 1ll);
if(t >= mod)
t %= mod;
t *= inv6;
if(t >= mod)
t %= mod;
t *= (2ll * n + 1ll);
if(t >= mod)
t %= mod;
return t;
}
unordered_map<int, int> Sum;
//杜教篩
inline int GetSum(int n) {
if(n <= MAXN)
return sum[n];
else if(Sum.count(n))
return Sum[n];
//f*g=id的前綴和
ll ret = s2(n);
for(int l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
ll tmp = (s1(r) - s1(l - 1));
if(tmp < 0)
tmp += mod;
tmp *= GetSum(n / l);
if(tmp >= mod)
tmp %= mod;
ret -= tmp;
if(ret < 0)
ret += mod;
}
return Sum[n] = ret; // 記憶化
}
inline int Ans(int n) {
ll tmp = GetSum(n) - 1;
if(tmp < 0)
tmp += mod;
tmp *= inv2;
if(tmp >= mod)
tmp %= mod;
return tmp;
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
sieve();
int t;
scanf("%d", &t);
while(t--) {
int n, a, b;
scanf("%d%d%d", &n, &a, &b);
printf("%d\n", Ans(n));
}
}
1006 Shuffle Card
一個非常搞的簽到題,一開始以為要用個Treap去手寫一些結構,寫到Remove的時候一想不是用個set就可以了嗎?
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
set<pair<int, int> > s;
int p[100005];
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
int ai;
scanf("%d", &ai);
p[ai] = i;
s.insert({p[ai], ai});
}
int cur = 0;
for(int i = 1; i <= m; ++i) {
int ai;
scanf("%d", &ai);
s.erase({p[ai], ai});
p[ai] = cur--;
s.insert({p[ai], ai});
}
for(auto si : s) {
printf("%d ", si.second);
}
}
但最后發現直接把輸入離線倒過來輸出就行了???
1007 Windows Of CCPC
分形簽到題,把隊友演了幾分鍾,下次不管階,直接算長度,想好再上機。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char g[1050][1050];
void draw(int x, int y, int len, char d1, char d2) {
if(len == 1) {
g[x][y] = d1;
return;
}
len >>= 1;
draw(x, y, len, d1, d2);
draw(x, y + len, len, d1, d2);
draw(x + len, y, len, d2, d1);
draw(x + len, y + len, len, d1, d2);
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int T;
scanf("%d", &T);
for(int i = 1; i <= T; ++i) {
int len;
scanf("%d", &len);
len = 1 << len;
memset(g, 0, sizeof(g));
draw(1, 1, len, 'C', 'P');
for(int i = 1; i <= len; ++i)
puts(g[i] + 1);
}
return 0;
}
1008 Fishing Master
當時一點都不會,zf力挽狂瀾。看了一下一個比較好的思路是這樣。
首先燉魚的總時間是固定的,記為T,就是要合理安排去抓魚的時間,也就是找一些空擋去抓魚。那么每條魚燉的時間就可以直接模k了,除以k的商就是額外抓魚的次數。假如額外抓魚的次數夠了n-1次,那么答案就是T+k。否則一定會有某些魚在燉的時候要去抓魚而浪費鍋時。
這里的貪心策略就是要浪費盡可能少的鍋時,每條魚的模k余數記為t,那么去抓魚的時候會浪費k-t,找最小的幾個浪費,直到補滿額外抓魚的次數。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
const int INF = 1e9;
int t[MAXN];
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int Ti;
scanf("%d", &Ti);
while(Ti--) {
int n, k;
scanf("%d%d", &n, &k);
ll sum = 0, T = k;
int mi = INF;
for(int i = 1; i <= n; ++i) {
scanf("%d", &t[i]);
T += t[i];
sum += t[i] / k;
mi = min(mi, t[i] / k);
//計算額外抓魚的次數
}
sum -= mi;
//提供額外抓魚次數最少的那條魚先抓起來,不會更差
if(sum >= n - 1) {
printf("%lld\n", T);
} else {
for(int i = 1; i <= n; ++i) {
t[i] = k - (t[i] % k);
//現在ti是每條魚燉的時候去抓要浪費的鍋的時間
}
sort(t + 1, t + 1 + n);
int i = 1;
while(sum < n - 1) {
T += t[i];
++sum;
++i;
}
printf("%lld\n", T);
}
}
return 0;
}