A - Everyone Loves Playing Games
首先将,然后将
的
删除,
修改为
。
问题转化为了选择或者不选择,构造线性基
。
假如对于的第
位都存在,设
的第
位线性基为
,
的第
位线性基为
。那么需要将
修改为
,将
插入线性基
。
最后的即为答案,正确的原因是将
插入线性基
等价于是反悔贪心。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e4 + 5;
int n, m;
struct LB
{
ll b[65];
void clear() { memset(b, 0, sizeof(b)); }
void ins(ll x)
{
for(int i=62; i>=0; i--)
if((x>>i)&1)
{
if(!b[i])
{
b[i] = x;
return;
}
x ^= b[i];
}
}
}A, B;
void solve()
{
scanf("%d%d", &n, &m);
ll X = 0;
A.clear(), B.clear();
for(int i=1; i<=n; i++)
{
ll x, y; scanf("%lld%lld", &x, &y);
X ^= x;
A.ins(x^y);
}
for(int i=1; i<=m; i++)
{
ll x, y; scanf("%lld%lld", &x, &y);
X ^= x;
B.ins(x^y);
}
ll ans = X;
for(int i=62; i>=0; i--)
{
if(!A.b[i] && !B.b[i]) continue;
if(!A.b[i]) ans = min(ans, ans^B.b[i]);
else if(!B.b[i]) ans = max(ans, ans^A.b[i]);
else
{
if((ans>>i)&1) ans ^= A.b[i];
A.ins(A.b[i]^B.b[i]);
}
}
printf("%lld\n", ans);
}
int main()
{
int _; scanf("%d", &_);
while(_--) solve();
return 0;
}
B - Gifted Composer
首先发现一个事情,长度的字符串在时间
的时刻是循环节的长度,并在接下来连续一段时间内都是循环节的长度,在某一时刻开始不是循环节的长度,直至结束都不再是循环节的长度。
这样就可以枚举循环节的长度并二分循环节的结束时间。找到结束时间后,在时间轴上差分即可。
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e6 + 10 ;
template<unsigned mod , unsigned base>
struct Hash
{
unsigned p[maxn] , pre[maxn] ;
Hash()
{
p[0] = 1 ;
for(int i = 1 ; i < maxn ; i ++) p[i] = 1ull * p[i - 1] * base % mod ;
pre[0] = 0 ;
}
void build(const char *s) //s[0 .. len - 1] <-> pre[1 .. len]
{
int len = strlen(s) ;
for(int i = 0 ; i < len ; i ++) pre[i + 1] = (1ull * pre[i] * base + s[i]) % mod ;
}
unsigned operator()(int l , int r) // pre[l + 1 ... r] <-> s[l , r - 1]
{
return (pre[r] - 1ull * pre[l] * p[r - l] % mod + mod) % mod ;
}
} ;
struct d_hash // double hash
{
Hash<997137961 , 753> h1 ;
Hash<1003911991 , 467> h2 ;
void build(const char *s)
{
h1.build(s) ;
h2.build(s) ;
}
uint64_t operator() (int l , int r) // pre[l + 1 ... r]
{
return uint64_t(h1(l , r)) << 32 | h2(l , r) ;
}
} hasher ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n ;
cin >> n ;
list<char> s ;
int l = 0 , r = 0 ;
vector<pair<int , int>> pos ;
for(int i = 1 ; i <= n ; i ++)
{
string s1 , s2 ;
cin >> s1 >> s2 ;
char c ;
//因为是判循环节种类数,所以循环节长度不重要,缩减至一个字符有助于卡常
if(s2 == "sol") c = 'z' ; //重复首字符
else c = s2[0] ;
if(s1 == "p")
{
s.push_front(c) ;
l ++ ;
}
else
{
s.push_back(c) ;
r ++ ;
}
pos.push_back({l , r}) ;
}
for(auto &u : pos) u.first = l - u.first , u.second = l + u.second - 1 ;
hasher.build(string(s.begin() , s.end()).c_str()) ;
vector<int> ans(n + 10 , 0) ;
auto ok = [&](int k , int mid)
{
int l = pos[mid - 1].first ;
int r = pos[mid - 1].second ;
return hasher(l , r - k + 1) == hasher(l + k , r + 1) ;
} ;
auto cal = [&](int l , int r) //二分查找结束时间
{
int res = l ;
int k = l ;
while(l <= r)
{
int mid = (l + r) / 2 ;
if(ok(k , mid)) res = mid , l = mid + 1 ;
else r = mid - 1 ;
}
return res ;
} ;
for(int i = 1 ; i <= n ; i ++)
{
//在时间轴上差分
ans[i] ++ ;
int t = cal(i , n) ;
ans[t + 1] -- ;
}
for(int i = 2 ; i <= n ; i ++) ans[i] += ans[i - 1] ;
for(int i = 1 ; i <= n ; i ++) cout << ans[i] << '\n' ;
return 0 ;
}
C - An Unsure Catch
留坑。
D - String Theory
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 3e5 + 10 ;
struct Sa //如果是多个测例,记得清空 s
{
int rk[maxn << 1] , sa[maxn << 1] , height[maxn << 1] ;
int tmp[maxn << 1] , cnt[maxn] ;
char s[maxn] ;
int st[maxn][25] ;
void cal(int n , int m)
{
n ++ ;
for(int i = 0 ; i < n * 2 + 5 ; i ++)
rk[i] = sa[i] = height[i] = tmp[i] = 0 ;//开2 倍空间
for(int i = 0 ; i < m ; i ++) cnt[i] = 0 ;
for(int i = 0 ; i < n ; i ++) cnt[rk[i] = s[i]] ++ ;
for(int i = 1 ; i < m ; i ++) cnt[i] += cnt[i - 1] ;
for(int i = 0 ; i < n ; i ++) sa[-- cnt[rk[i]]] = i ;
for(int k = 1 ; k <= n ; k <<= 1)
{
int j = 0 ;
for(int i = 0 ; i < n ; i ++)
{
j = sa[i] - k ;
if(j < 0) j += n ;
tmp[cnt[rk[j]] ++] = j ;
}
sa[tmp[cnt[0] = 0]] = j = 0 ;
for(int i = 1 ; i < n ; i ++)
{
if(rk[tmp[i]] != rk[tmp[i - 1]]
|| rk[tmp[i] + k] != rk[tmp[i - 1] + k])
cnt[++ j] = i ;
sa[tmp[i]] = j ;
}
memcpy(rk , sa , n * sizeof(int)) ;
memcpy(sa , tmp , n * sizeof(int)) ;
if(j >= n - 1) break ;
}
height[0] = 0 ;
for(int i = 0 , k = 0 , j = rk[0] ; i < n - 1 ; i ++ , k ++)
while(~k && s[i] != s[sa[j - 1] + k])
height[j] = k -- , j = rk[sa[j] + 1] ;
}
void build_lcp(int n)
{
for(int i = 1 ; i <= n ; i ++) st[i][0] = height[i] ;
for(int j = 1 ; j <= 20 ; j ++)
for(int i = 1 ; i + (1 << j) - 1 <= n ; i ++)
st[i][j] = min(st[i][j - 1] , st[i + (1 << (j - 1))][j - 1]) ;
}
int lcp(int l , int r)
{
if(l > r) swap(l , r) ;
l ++ ;
int len = log2(r - l + 1) ;
return min(st[l][len] , st[r - (1 << len) + 1][len]) ;
}
} sa[2] ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int T ;
cin >> T ;
while(T --)
{
int k ;
cin >> k ;
cin >> sa[0].s ;
int len = strlen(sa[0].s) ;
if(k == 1)
{
cout << 1ll * len * (len + 1) / 2 << '\n' ;
continue ;
}
for(int i = 0 ; i < len ; i ++) sa[1].s[i] = sa[0].s[len - 1 - i] ;
sa[0].cal(len , 200) ;
sa[0].build_lcp(len) ;
sa[1].cal(len , 200) ;
sa[1].build_lcp(len) ;
auto rev = [&](int x)
{
return len - 1 - x ;
} ;
long long ans = 0 ;
for(int i = 1 ; i <= len / k ; i ++)
{
for(int j = 0 ; j < len ; j += i)
{
int p = j ;
while(p + i < len && sa[0].lcp(sa[0].rk[p] , sa[0].rk[p + i]) >= i) p += i ;
if(p + i - 1 <= len - 1 && (p == j || sa[0].lcp(sa[0].rk[j] , sa[0].rk[p]) >= i)) p += i ;
if((p - j) / i >= k - 1)
{
int x = p ;
int lcp1 = 0 ;
if(x < len) lcp1 = sa[0].lcp(sa[0].rk[j] , sa[0].rk[x]) ;
int y = j - 1 ;
int lcp2 = 0 ;
if(y >= 0) lcp2 = sa[1].lcp(sa[1].rk[rev(j - 1)] , sa[1].rk[rev(j + i - 1)]) ;
ans += max(0 , lcp1 + lcp2 + p - j - i * k + 1) ;
}
if(p > j) j = p - i ;
}
}
cout << ans << '\n' ;
}
return 0 ;
}
E - Road Construction
留坑。
F - Girlfriend
留坑。
G - Blackjack
定义表示前
张牌选择了
张牌且权值和是
的概率。转移方程是
题意要求在选择张牌后权值和
且在选择
牌后权值和
。
因此需要枚举最后一张选择的牌是什么,即第张牌是什么。这样复杂度是
。
这个背包是加法的,所以可以枚举撤销最后一张选择的牌的贡献,计算后可以是可以复原最后一张选择的牌的贡献的。这样复杂度是。
#include<bits/stdc++.h>
using namespace std ;
typedef double db ;
const int maxn = 500 + 10 ;
db dp[2][maxn][maxn] ; //dp[i][j][k]表示前i个数选择j张且权值和是k的概率
db tmp[maxn][maxn] ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n , a , b ;
cin >> n >> a >> b ;
vector<int> x(n + 1 , 0) ;
for(int i = 1 ; i <= n ; i ++) cin >> x[i] ;
dp[0][0][0] = 1.0 ;
int op = 0 ;
for(int i = 1 ; i <= n ; i ++)
{
op = 1 - op ;
for(int j = 0 ; j <= i ; j ++)
for(int k = 0 ; k <= 500 ; k ++)
{
dp[op][j][k] = dp[1 - op][j][k] ;
if(j >= 1 && k >= x[i]) dp[op][j][k] += dp[1 - op][j - 1][k - x[i]] * j / (n + 1 - j) ;
}
}
db ans = 0.0 ;
for(int i = 1 ; i <= n ; i ++)
{
//撤销
for(int j = 0 ; j < n ; j ++)
for(int k = 0 ; k <= 500 ; k ++)
{
tmp[j][k] = dp[op][j][k] ;
if(j >= 1 && k >= x[i])
tmp[j][k] -= tmp[j - 1][k - x[i]] * j / (n + 1 - j) ;
}
//计算
for(int j = 0 ; j < n ; j ++)
for(int k = 0 ; k <= 500 ; k ++)
{
if(a - x[i] < k && k <= min(b - x[i] , a))
ans += tmp[j][k] / (n - j) ;
}
}
cout << fixed << setprecision(12) << ans << '\n' ;
return 0 ;
}
H - Yet Another Geometry Problem
留坑。
I - A Math Problem
留坑。
J - Gaokao
温暖的签到题。
#include<bits/stdc++.h>
using namespace std ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int T ;
cin >> T ;
while(T --)
{
long long n ;
cin >> n ;
cout << (1ll << __builtin_popcountll(n - 1)) << '\n' ;
}
return 0 ;
}
K - Data Structure
留坑。
L - Landlord
离散化后判连通块个数。
#include <bits/stdc++.h>
using namespace std;
const int N = 25;
struct Rec
{
int x1, y1, x2, y2;
void input() { scanf("%d%d%d%d", &x1, &y1, &x2, &y2); }
}p[2];
bool tag[N][N];
int upx, upy;
const int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
void dfs(int x, int y)
{
tag[x][y] = 1;
for(int i=0; i<4; i++)
{
int tx = x + dir[i][0], ty = y + dir[i][1];
if(tx>=0&&tx<=upx&&ty>=0&&ty<=upy&&!tag[tx][ty]) dfs(tx, ty);
}
}
void solve()
{
p[0].input(); p[1].input();
vector<int> x, y;
for(int i=0; i<2; i++) x.push_back(p[i].x1<<1), x.push_back(p[i].x2<<1), x.push_back(p[i].x1<<1|1), x.push_back(p[i].x2<<1|1);
for(int i=0; i<2; i++) y.push_back(p[i].y1<<1), y.push_back(p[i].y2<<1), y.push_back(p[i].y1<<1|1), y.push_back(p[i].y2<<1|1);
sort(begin(x), end(x)); sort(begin(y), end(y));
x.erase(unique(begin(x), end(x)), end(x));
y.erase(unique(begin(y), end(y)), end(y));
memset(tag, 0, sizeof(tag));
for(int i=0; i<2; i++)
{
p[i].x1 = lower_bound(begin(x), end(x), p[i].x1<<1) - begin(x) + 1;
p[i].y1 = lower_bound(begin(y), end(y), p[i].y1<<1) - begin(y) + 1;
p[i].x2 = lower_bound(begin(x), end(x), p[i].x2<<1) - begin(x) + 1;
p[i].y2 = lower_bound(begin(y), end(y), p[i].y2<<1) - begin(y) + 1;
for(int a=p[i].x1; a<=p[i].x2; a++) tag[a][p[i].y1] = tag[a][p[i].y2] = 1;
for(int b=p[i].y1; b<=p[i].y2; b++) tag[p[i].x1][b] = tag[p[i].x2][b] = 1;
}
upx = x.size() + 1, upy = y.size() + 1;
// for(int i=0; i<=upx; i++)
// for(int j=0; j<=upy; j++) cout << tag[i][j] << " \n"[j==upy];
int ans = 0;
for(int i=0; i<=upx; i++)
for(int j=0; j<=upy; j++)
if(!tag[i][j]) ++ans, dfs(i, j);
printf("%d\n", ans);
}
int main()
{
int _; scanf("%d", &_);
while(_--) solve();
return 0;
}
M - Travel Dream
留坑。