A. Replacing Elements
用最小的两个数的和去替换比它大的数。
#include<bits/stdc++.h>
using namespace std ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int T ;
cin >> T ;
while(T --)
{
int n , d ;
cin >> n >> d ;
vector<int> a(n) ;
for(int i = 0 ; i < n ; i ++) cin >> a[i] ;
sort(a.begin() , a.end()) ;
int mn = a[0] + a[1] ;
for(auto &u : a) u = min(u , mn) ;
bool flag = 1 ;
for(auto u : a) if(u > d) flag = 0 ;
if(flag) cout << "YES\n" ;
else cout << "NO\n" ;
}
return 0 ;
}
B. String LCM
暴力
#include<bits/stdc++.h>
using namespace std ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int T ;
cin >> T ;
while(T --)
{
string s , t ;
cin >> s >> t ;
int len = (s.size() * t.size()) / (__gcd(s.size() , t.size())) ;
int cnt1 = len / s.size() ;
int cnt2 = len / t.size() ;
string tmp1 = "" ;
for(int i = 0 ; i < cnt1 ; i ++) tmp1 += s ;
string tmp2 = "" ;
for(int i = 0 ; i < cnt2 ; i ++) tmp2 += t ;
if(tmp1 != tmp2) cout << "-1\n" ;
else cout << tmp1 << '\n' ;
}
return 0 ;
}
C. No More Inversions
无力吐槽,做不来这种题。找一个小时找不到规律,打表五分钟解决战斗。
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 2e5 + 10 ;
int n , k ;
int a[maxn] , p[maxn] ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int T ;
cin >> T ;
while(T --)
{
cin >> n >> k ;
for(int i = 1 ; i <= n ; i ++) if(i <= k) a[i] = i ; else a[i] = k - (i - k) ;
int x = k - (n - k) ;
p[x] = k ;
for(int i = 1 ; i <= k ; i ++)
if(i < x) p[i] = i ;
else if(i > x) p[i] = k - (i - x) ;
for(int i = 1 ; i <= k ; i ++) cout << p[i] << " \n"[i == k] ;
}
return 0 ;
}
D. Program
进行前次操作后,当前的值是。其中是前次操作的“+”的个数。容易发现进行若干次操作获得的值是连续的,那么只要知道最大值和最小值即可。
线段树维护这个东西,并且支持区间最值查询即可。
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 2e5 + 10 ;
char op[maxn] ;
int pre[maxn] ;
int n , m ;
struct seg_tree
{
int min1[maxn << 2] , lazy[maxn << 2] ;
int ls(int x){ return x << 1 ; }
int rs(int x){ return x << 1 | 1 ; }
void push_up(int p){ min1[p] = min(min1[ls(p)] , min1[rs(p)]) ; }
void build(int id , int l , int r)
{
lazy[id] = 0 ;
if(l == r) {min1[id] = 2 * pre[l] - l ; return ;}
int mid = (l + r) >> 1 ;
build(ls(id) , l , mid) ;
build(rs(id) , mid + 1 , r) ;
push_up(id) ;
}
void f(int id , int l , int r , int k)
{
lazy[id] += k ;
min1[id] += k ;
}
void push_down(int id , int l , int r)
{
int mid = (l + r) >> 1 ;
f(ls(id) , l , mid , lazy[id]) ;
f(rs(id) , mid + 1 , r , lazy[id]) ;
lazy[id] = 0 ;
}
void add(int id , int l , int r , int x , int y , int k)
{
if(x > y || x > r || y < l) return ;
if(x <= l && r <= y)
{
min1[id] += k ;
lazy[id] += k ;
return ;
}
push_down (id , l , r) ;
int mid = (l + r) >> 1 ;
if(x <= mid) add(ls(id) , l , mid , x , y , k) ;
if(y > mid) add(rs(id) , mid + 1 , r , x , y , k) ;
push_up(id) ;
}
int query_mn(int id , int l , int r , int x , int y)
{
if(x > y || x > r || y < l) return 1e9 ;
int ans = 1e9 ;
if(x <= l && r <= y) return min1[id] ;
int mid = (l + r) >> 1 ;
push_down(id , l , r) ;
if(x <= mid) ans = min(ans , query_mn(ls(id) , l , mid , x , y)) ;
if(y > mid) ans = min(ans , query_mn(rs(id) , mid + 1 , r , x , y)) ;
return ans ;
}
} seg ;
struct seg_tree2
{
int max1[maxn << 2] , lazy[maxn << 2] ;
int ls(int x){ return x << 1 ; }
int rs(int x){ return x << 1 | 1 ; }
void push_up(int p){ max1[p] = max(max1[ls(p)] , max1[rs(p)]) ; }
void build(int id , int l , int r)
{
lazy[id] = 0 ;
if(l == r) {max1[id] = 2 * pre[l] - l ; return ;}
int mid = (l + r) >> 1 ;
build(ls(id) , l , mid) ;
build(rs(id) , mid + 1 , r) ;
push_up(id) ;
}
void f(int id , int l , int r , int k)
{
lazy[id] += k ;
max1[id] += k ;
}
void push_down(int id , int l , int r)
{
int mid = (l + r) >> 1 ;
f(ls(id) , l , mid , lazy[id]) ;
f(rs(id) , mid + 1 , r , lazy[id]) ;
lazy[id] = 0 ;
}
void add(int id , int l , int r , int x , int y , int k)
{
if(x > y || x > r || y < l) return ;
if(x <= l && r <= y)
{
max1[id] += k ;
lazy[id] += k ;
return ;
}
push_down (id , l , r) ;
int mid = (l + r) >> 1 ;
if(x <= mid) add(ls(id) , l , mid , x , y , k) ;
if(y > mid) add(rs(id) , mid + 1 , r , x , y , k) ;
push_up(id) ;
}
int query_mx(int id , int l , int r , int x , int y)
{
if(x > y || x > r || y < l) return 0 ;
int ans = 0 ;
if(x <= l && r <= y) return max1[id] ;
int mid = (l + r) >> 1 ;
push_down(id , l , r) ;
if(x <= mid) ans = max(ans , query_mx(ls(id) , l , mid , x , y)) ;
if(y > mid) ans = max(ans , query_mx(rs(id) , mid + 1 , r , x , y)) ;
return ans ;
}
} seg2 ;
void init()
{
for(int i = 1 ; i <= n ; i ++) pre[i] = pre[i - 1] + (op[i] == '+') ;
seg.build(1 , 1 , n) ;
seg2.build(1 , 1 , n) ;
}
void upd(int l , int r , int k)
{
seg.add(1 , 1 , n , l , r , k) ;
seg2.add(1 , 1 , n , l , r , k) ;
}
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int T ;
cin >> T ;
while(T --)
{
cin >> n >> m ;
cin >> op + 1 ;
init() ;
while(m --)
{
int l , r ;
cin >> l >> r ;
int cnt = pre[r] - pre[l - 1] ;
int len = r - l + 1 ;
upd(l + 1 , n , -2 * cnt + len) ;
int mx = max({0 , seg2.query_mx(1 , 1 , n , 1 , l - 1) , seg2.query_mx(1 , 1 , n , r + 1 , n)}) ;
int mn = min({0 , seg.query_mn(1 , 1 , n , 1 , l - 1) , seg.query_mn(1 , 1 , n , r + 1 , n)}) ;
cout << mx - mn + 1 << '\n' ;
upd(l + 1 , n , 2 * cnt - len) ;
}
}
return 0 ;
}
E. Minimum Path
将一条边的费用加倍,另一条边的费用是0。使用这两个操作,状压跑最短路即可。
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 2e5 + 10 ;
typedef pair<long long , pair<int , int> > plii ; //边权long long
struct Link
{
int num , head[maxn] ;
struct Edge
{
int v , next ;
long long w ;
} edge[maxn << 1] ;
void init()
{
num = 0 ;
memset(head , -1 , sizeof(head)) ;
}
void add_edge(int u , int v , long long w)
{
edge[num].v = v ;
edge[num].w = w ;
edge[num].next = head[u] ;
head[u] = num ++ ;
}
} link ;
struct Dij
{
long long dis[maxn][4] ;
priority_queue<plii , vector<plii> , greater<plii> > q ;
void init()
{
memset(dis , 0x3f , sizeof(dis)) ;
}
void dijkstra(int s)
{
dis[s][0] = 0 ;
q.push(make_pair(0 , make_pair(s , 0))) ;
while(!q.empty())
{
plii p = q.top() ;
q.pop() ;
int u = p.second.first ;
int s = p.second.second ;
if(p.first != dis[u][s]) continue ; //优化,不用旧值更新。
for(int i = link.head[u] ; i != -1 ; i = link.edge[i].next)
{
int v = link.edge[i].v ;
long long w = link.edge[i].w ;
//0
if(dis[v][s] > dis[u][s] + w)
{
dis[v][s] = dis[u][s] + w ;
q.push(make_pair(dis[v][s] , make_pair(v , s))) ;
}
//1
if(!(s & 1))
{
if(dis[v][s | 1] > dis[u][s] + 0)
{
dis[v][s | 1] = dis[u][s] + 0 ;
q.push(make_pair(dis[v][s | 1] , make_pair(v , s | 1))) ;
}
}
//2
if(!(s & 2))
{
if(dis[v][s | 2] > dis[u][s] + 2 * w)
{
dis[v][s | 2] = dis[u][s] + 2 * w ;
q.push(make_pair(dis[v][s | 2] , make_pair(v , s | 2))) ;
}
}
}
}
}
} dij ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n , m ;
cin >> n >> m ;
link.init() ;
for(int i = 1 ; i <= m ; i ++)
{
int u , v , w ;
cin >> u >> v >> w ;
link.add_edge(u , v , w) ;
link.add_edge(v , u , w) ;
}
dij.init() ;
dij.dijkstra(1) ;
for(int i = 2 ; i <= n ; i ++) cout << min(dij.dis[i][0] , dij.dis[i][3]) << " \n"[i == n] ;
return 0 ;
}
F. Strange Set
最大权闭合子图模板题。注意对于相同的,和最近的连边即可,最小割是不变的。
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 6e5 + 10 ;
const int inf = 0x3f3f3f3f ;
struct Dinic
{
int tot ;
int Next[maxn],last[maxn],to[maxn],flow[maxn],q[maxn],dis[maxn],S,T;
void init()
{
tot = 1 ;
memset(last , 0 , sizeof(last)) ;
}
void add(int x,int y,int v) {
Next[++tot]=last[x]; last[x]=tot; to[tot]=y; flow[tot]=v;
}
void auto_add(int x,int y,int w) {
add(x,y,w);
add(y,x,0);
}
bool bfs() {
int l=0,r=1; q[1]=S;
for (int i=1;i<=T;i++) dis[i]=0;
dis[S]=1;
while (l<r) {
int k=q[++l];
for (int i=last[k];i;i=Next[i]) {
if (dis[to[i]]||!flow[i]) continue;
dis[q[++r]=to[i]]=dis[k]+1;
}
}
return dis[T]>0;
}
int dfs(int x,int y) {
if (!y) return 0;
if (x==T) return y;
int sum=0;
for (int i=last[x];i;i=Next[i]) {
if (dis[to[i]]!=dis[x]+1||!flow[i]) continue;
int tmp=dfs(to[i],min(y-sum,flow[i]));
if (tmp) {
sum+=tmp;
flow[i]-=tmp;
flow[i^1]+=tmp;
}
else dis[to[i]]=0;
if (sum==y) return sum;
}
return sum;
}
int dinic() {
int ans=0;
while (bfs()) ans+=dfs(S,inf);
return ans;
}
} dinic ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n ;
cin >> n ;
vector<int> a(n + 1) , b(n + 1) ;
for(int i = 1 ; i <= n ; i ++) cin >> a[i] ;
for(int i = 1 ; i <= n ; i ++) cin >> b[i] ;
dinic.init() ;
dinic.S = n + 1 ;
dinic.T = n + 2 ;
int ans = 0 ;
for(int i = 1 ; i <= n ; i ++)
{
if(b[i] > 0)
{
ans += b[i] ;
dinic.auto_add(dinic.S , i , b[i]) ;
}
else if(b[i] < 0)
{
dinic.auto_add(i , dinic.T , -b[i]) ;
}
}
vector<int> lst(101 , -1) ;
for(int i = 1 ; i <= n ; i ++)
{
for(int j = 1 ; j <= 100 ; j ++)
if(a[i] % j == 0)
{
if(lst[j] != -1)
dinic.auto_add(i , lst[j] , inf) ;
}
lst[a[i]] = i ;
}
cout << ans - dinic.dinic() << '\n' ;
return 0 ;
}
G. Tiles
很容易发现和组合数的关系,这题就是跑1000次NTT。
卷积的式子很好写,代码不是很好写,因为有负数的情况,需要手动添加偏移量。虽然就两行,还是想了半天。竟然在C++17 TLE了,改成C++17(64)就过了,不过只卡了我一分钟,要不然就正无穷了。
具体细节就看官方题解吧。
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define cl(x) x.clear()
#define all(x) x.begin() , x.end()
#define rep(i , x , n) for(int i = x ; i <= n ; i ++)
#define per(i , n , x) for(int i = n ; i >= x ; i --)
#define mem0(x) memset(x , 0 , sizeof(x))
#define mem_1(x) memset(x , -1 , sizeof(x))
#define mem_inf(x) memset(x , 0x3f , sizeof(x))
#define debug(x) cerr << #x << " = " << x << '\n'
#define ddebug(x , y) cerr << #x << " = " << x << " " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int maxn = 2e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ;
const int mod = 998244353 ;
struct NTT
{
int n , m ;
ll a[maxn << 2] , b[maxn << 2] ;
ll up , l ;
ll pos[maxn << 2] ;
ll powmod(ll a , ll b)
{
ll ans = 1 ;
while(b)
{
if(b & 1) ans = ans * a % mod ;
a = a * a % mod ;
b >>= 1 ;
}
return ans ;
}
void init(int n , int m)
{
up = 1 , l = 0 ;
while(up < (n + m)) up <<= 1 , l ++ ;
rep(i , 0 , up - 1) pos[i] = (pos[i >> 1] >> 1) | ((i & 1) << (l - 1)) , a[i] = b[i] = 0 ;
}
void solve(ll *a , int mode)
{
rep(i , 0 , up - 1) if(i < pos[i]) swap(a[i] , a[pos[i]]) ;
for(int i = 1 ; i < up ; i <<= 1)
{
ll gn = powmod(3 , (mod - 1) / (i << 1)) ;
if(mode == -1) gn = powmod(gn , mod - 2) ;
for(int j = 0 ; j < up ; j += (i << 1))
{
ll g = 1 ;
for(int k = 0 ; k < i ; k ++ , g = g * gn % mod)
{
ll x = a[j + k] , y = g * a[j + k + i] % mod ;
a[j + k] = (x + y) % mod , a[j + k + i] = (x - y + mod) % mod ;
}
}
}
if(mode == -1)
{
ll invup = powmod(up , mod - 2) ;
rep(i , 0 , up - 1) a[i] = a[i] * invup % mod ;
}
}
} ntt ;
struct Easymath
{
ll qpow(ll a , ll b) //快速幂
{
if(b < 0) return 0 ;
ll ans = 1 ;
a %= mod ;
while(b)
{
if(b & 1) ans = (ans * a) % mod ;
b >>= 1 , a = (a * a) % mod ;
}
return ans % mod ;
}
ll ksc_log(ll x , ll y , ll mod) //快速乘
{
x %= mod , y %= mod ;
ll ans = 0;
while(y)
{
if(y & 1) ans = (ans + x) % mod ;
y >>= 1 ;
x = (x + x) % mod ;
}
return ans;
}
ll ksc_O1(ll x , ll y , ll mod) //快速乘
{
x %= mod , y %= mod ;
ll z = (ld)x * y / mod ;
ll ans = x * y - z * mod ;
if(ans < 0) ans += mod ;
else if(ans >= mod) ans -= mod ;
return ans ;
}
int cnt = 0 ;
bool vis[maxn] ;
int prime[maxn] ;
void get_prime(int up) //素数筛
{
memset(vis , 0 , sizeof(vis)) ;
vis[1] = 1 ;
for(int i = 2 ; i <= up ; i ++)
{
if(!vis[i])
prime[++ cnt] = i ;
for(int j = 1 ; j <= cnt && i * prime[j] <= up ; j ++)
{
vis[i * prime[j]] = 1 ;
if(i % prime[j] == 0) break ;
}
}
}
//begin 判定大素数
ll mul(ll a , ll b , ll mod)
{
ll ret = 0 ;
while(b)
{
if(b & 1) ret = (ret + a) % mod ;
a = (a + a) % mod ;
b >>= 1 ;
}
return ret ;
}
ll pow(ll a , ll b , ll mod)
{
ll ret = 1 ;
while(b)
{
if(b & 1) ret = mul(ret , a , mod) ;
a = mul(a , a , mod) ;
b >>= 1 ;
}
return ret ;
}
bool check(ll a , ll n)
{
ll x = n - 1 ;
int t = 0 ;
while((x & 1) == 0)
{
x >>= 1 ;
t ++ ;
}
x = pow(a , x , n) ;
ll y ;
rep(i , 1 , t)
{
y = mul(x , x , n) ;
if(y == 1 && x != 1 && x != n - 1) return 1 ;
x = y ;
}
if(y != 1) return 1 ;
return 0 ;
}
bool Miller_Rabin(ll n)
{
if(n == 2) return 1 ;
if(n == 1 || !(n & 1)) return 0 ;
const int arr[12] = {2,3,5,7,11,13,17,19,23,29,31,37} ;
rep(i , 0 , 11)
{
if(arr[i] >= n) break ;
if(check(arr[i] , n)) return 0 ;
}
return 1 ;
}
//end 判定大素数
ll get_inv(ll x) //逆元
{
return qpow(x , mod - 2) % mod ;
}
ll inv1[maxn] ; //乘法逆元
void init1(int up)
{
inv1[1] = 1 ;
for(int i = 2 ; i <= up ; i ++)
inv1[i] = (ll)(mod - mod / i) * inv1[int(mod % (ll)i)] % mod ;
}
ll fac[maxn] ;
ll inv[maxn] ; //阶乘逆元
void init(int up)
{
fac[0] = fac[1] = inv[0] = inv[1] = 1 ;
for(int i = 2 ; i <= up ; i ++)
{
fac[i] = fac[i - 1] * i % mod ;
inv[i] = -inv[mod % i] * (mod / i) % mod ;
while(inv[i] < 0) inv[i] += mod ;
}
for(int i = 2 ; i <= up ; i ++)
inv[i] = inv[i] * inv[i - 1] % mod ;
}
ll C(int n , int m)
{
if(m < 0 || n < m) return 0 ;
return fac[n] * inv[m] % mod * inv[n - m] % mod ;
}
} em ;
int main()
{
ios ;
int n ;
cin >> n ;
vector<int> dp(10005 , 0) ;
em.init(200000) ;
int m = 1 ;
dp[1] = 1 ;
while(n --)
{
int a , b ;
cin >> a >> b ;
ntt.init(m + 1 , m + a - b - 1 + m + 1) ;
for(int i = 1 ; i <= m ; i ++) ntt.a[i] = dp[i] ;
for(int i = 1 ; i <= m + a - b - 1 + m ; i ++) ntt.b[i] = em.C(a + b , b + i - m) ;
ntt.solve(ntt.a , 1) ;
ntt.solve(ntt.b , 1) ;
rep(i , 0 , ntt.up - 1) ntt.a[i] *= ntt.b[i] , ntt.a[i] %= mod ;
ntt.solve(ntt.a , -1) ;
for(int i = 1 ; i <= m + a - b ; i ++) dp[i] = ntt.a[i + m] ;
m += a - b ;
}
long long ans = 0 ;
for(int i = 1 ; i <= m ; i ++) ans += dp[i] , ans %= mod ;
cout << ans << '\n' ;
return 0 ;
}