D. Tokitsukaze and Slash Draw
多重背包解法
题意:将物品移动n-k步的最小价值,移动ai步需要花费bi (若>=n则%n)
暴力TLE O(n^2*m)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define all(x) x.begin(),x.end()
#define no cout<<"No"<<endl
#define yes cout<<"Yes"<<endl
#define endl '\n'
// #define x first
// #define y second
typedef pair<int,int> PII;
const int N=200010;
const int mod=998244353;
const int INF=0x3f3f3f3f;
void solve(){
int m,n,k;cin>>n>>m>>k;
int v[m]={};int w[m]={};
for(int i=0;i<m;i++){
cin>>v[i]>>w[i];
}
vector<int>dp(n+1,1e18);
dp[k]=0;
for(int i=0;i<m;i++){
for(int p=0;p<n;p++){
for(int j=0;j<=n;j++){
dp[j]=min(dp[j],dp[(j-v[i]+n)%n]+w[i]);
}
}
}
if(dp[n]==1e18)cout<<-1<<endl;
else cout<<dp[n]<<endl;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int _=1;
cin>>_;
while(_--)solve();
return 0;
}
二进制优化 O(nmlogn)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define all(x) x.begin(),x.end()
#define no cout<<"No"<<endl
#define yes cout<<"Yes"<<endl
#define endl '\n'
// #define x first
// #define y second
typedef pair<int,int> PII;
const int N=200010;
const int mod=998244353;
const int INF=0x3f3f3f3f;
void solve(){
int m,n,k;cin>>n>>m>>k;
vector<int>v;
vector<int>w;
for(int i=0;i<m;i++){
int a,b;cin>>a>>b;
int t=n-1;
if(n%a==0){
t=n/a-1;
}
int cnt=1;
while(t>cnt){
v.push_back(cnt*a);
w.push_back(cnt*b);
t-=cnt;
cnt*=2;
}
if(t>0){
v.push_back(t*a);
w.push_back(t*b);
}
}
vector<int>dp(n+1,1e18);
dp[0]=0;
for(int i=0;i<v.size();i++){
for(int j=0;j<=n;j++){
dp[j]=min(dp[j],dp[((j-v[i])%n+n)%n]+w[i]);
}
}
if(dp[n-k]==1e18)cout<<-1<<endl;
else cout<<dp[n-k]<<endl;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int _=1;
cin>>_;
while(_--)solve();
return 0;
}
此题不能用单调队列优化,因为取模的关系,导致状态转移没有单调性
图论解法 O(nm+n^2)
可以发现,此题为起点为 k 终点为 n 的最短路
不要使用堆优化,因为是稠密图
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define all(x) x.begin(),x.end()
#define no cout<<"No"<<endl
#define yes cout<<"Yes"<<endl
#define endl '\n'
#define x first
#define y second
typedef pair<int,int> PII;
const int N=5050;
const int mod=998244353;
const int INF=1e18;
int n,m,k;
void solve(){
cin>>n>>m>>k;
PII edge[m];
for(int i=0;i<m;i++){
int a,b;cin>>a>>b;
edge[i]={a,b};
}
int d[n+1]={};
int book[n+1]={};
for(int i=1;i<=n;i++)d[i]=1e18;
d[k]=0;
for(int p=0;p<n;p++){
int mn=INF;int t=k;
for(int i=1;i<=n;i++){//找最近的点
if(book[i]==0&&d[i]<=mn){
mn=d[i];
t=i;
}
}
if(t==n)break;
book[t]=1;
for(int j=0;j<m;j++){//松弛出边
if(book[(t+edge[j].x+n-1)%n+1])continue;
d[(t+edge[j].x+n-1)%n+1]=min(d[(t+edge[j].x-1+n)%n+1],d[t]+edge[j].y);
}
}
if(d[n]==1e18)cout<<-1<<endl;
else cout<<d[n]<<endl;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int _=1;
cin>>_;
while(_--)solve();
return 0;
}
C. Tokitsukaze and Min-Max XOR
关键点: 与a的相对位置没有关系,所以当max=ai,min=aj,且ai⊕aj<=k时,中间数可以随便选,那么贡献就为 2^i-j-1
暴力枚举最大最小值为O(n^2),trie树优化为O(nlogn):
对于一个ai,其贡献可能为 ,所以trie中维护累加
故对于一个ai其贡献为: ans+=ksm(2,i-1)*query(a[i]);
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define all(x) x.begin()+1,x.end()
#define endl '\n'
const int N=200010;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
unordered_map<int,int>mp;
unordered_map<int,int>mp2;
int ksm(int a,int n){
int r=1ll;
while(n){
if(n&1)r=r*a%mod;
a=a*a%mod;
n>>=1;
}
return r;
}
int inv(int a){
return ksm(a,mod-2);
}
void solve(){
int n,k;cin>>n>>k;
int nx[n*30][2]={},val[n*30]={},idx=1;
vector<int>a(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
sort(all(a));
auto insert=[&](int x,int pos){
int t=0;//初始为根节点0
for(int i=30;i>=0;i--){
int j=x>>i&1;
if(!nx[t][j])nx[t][j]=idx++;
t=nx[t][j];
val[t]=(val[t]+mp2[pos])%mod;
}
};
auto query=[&](int x){
int t=0,res=0;
for(int i=30;i>=0;i--){
int j=x>>i&1;
if(k>>i&1){
if(nx[t][j])res=(res+val[nx[t][j]])%mod;
if(nx[t][!j]){
t=nx[t][!j];
}else return res;
}else{
if(nx[t][j]){
t=nx[t][j];
}else return res;
}
}
res=(res+val[t])%mod;
return res;
};
int ans=0;
for(int i=2;i<=n;i++){
insert(a[i-1],i-1);
ans=(ans+mp[i-1]*query(a[i])%mod)%mod;
}
cout<<(ans+n)%mod<<endl;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int _=1;
cin>>_;
int t=1;
for(int i=0;i<=N;i++){
mp[i]=t;
mp2[i]=inv(t);
t=t*2%mod;
}
while(_--)solve();
return 0;
}
G. Tokitsukaze and Power Battle (easy)
不难发现此题本质是:
线段树中存每个点的pre[i]-a[i+1]
区间修改:维护 [l,r] 区间的 pre[i]-a[i+1]
区间查询:查询区间的最大值
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define all(x) x.begin(),x.end()
#define no cout<<"No"<<endl
#define yes cout<<"Yes"<<endl
#define endl '\n'
// #define x first
// #define y second
typedef pair<int,int> PII;
const int N=200010;
const int mod=998244353;
const int INF=0x3f3f3f3f3f3f3f3f;
struct SegmentTree
{
struct Node
{
int l, r;
int val, lz;
};
vector<Node>tr;
SegmentTree(int n) {
tr.resize(4 * n + 5);
}
void pushup(int t)
{
tr[t].val = max(tr[t * 2].val, tr[t * 2 + 1].val);
}
void pushdown(int t)
{
if (tr[t].lz) {
tr[2 * t].val += tr[t].lz;
tr[2 * t + 1].val += tr[t].lz;
tr[2 * t].lz += tr[t].lz;
tr[2 * t + 1].lz += tr[t].lz;
tr[t].lz = 0;
}
}
template<typename M>
void build(int t, int l, int r, vector<M>& a)
{
tr[t].l = l, tr[t].r = r;
if (l == r) {
tr[t].val = a[l];
return;
}
int mid = tr[t].l + tr[t].r >> 1;
build(t * 2, l, mid, a); build(t * 2 + 1, mid + 1, r, a);
pushup(t);
}
template<typename M>
void modify(int t, int x, M val) {
if (tr[t].l == tr[t].r) {
tr[t].val += val ;
tr[t].lz += val;
return;
}
pushdown(t);
int mid = tr[t].l + tr[t].r >> 1;
if (x <= mid) modify(t * 2, x, val);
else modify(t * 2 + 1, x, val);
pushup(t);
}
template<typename M>
void modify(int t, int l, int r, M val) {
if (l <= tr[t].l && tr[t].r <= r) {
tr[t].val += val ;
tr[t].lz += val;
return;
}
pushdown(t);
int mid = tr[t].l + tr[t].r >> 1;
if (l <= mid) modify(t * 2, l, r, val);
if (r > mid) modify(t * 2 + 1, l, r, val);
pushup(t);
}
int query(int t, int l, int r) {
if (l <= tr[t].l && tr[t].r <= r) {
return tr[t].val;
}
pushdown(t);
int mid = tr[t].l + tr[t].r >> 1;
int mx = -INF;
if (l <= mid) mx = max(mx, query(t * 2, l, r));
if (r > mid) mx = max(mx, query(t * 2 + 1, l, r));
return mx;
}
};
void solve(){
int n,q;cin>>n>>q;
vector<int>a(n+1);
vector<int>pre(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
pre[i]=pre[i-1]+a[i];
}
for(int i=1;i<=n-1;i++){
pre[i]-=a[i+1];
}
SegmentTree tr(n);
tr.build(1,1,n-1,pre);
while(q--){
int op;cin>>op;
if(op==1){
int x,y;cin>>x>>y;
if(x-1>=1)tr.modify(1,x-1,a[x]-y);
if(x<=n-1)tr.modify(1,x,n-1,y-a[x]);
a[x]=y;
}else{
int l,r;cin>>l>>r;
if(l==1)cout<<tr.query(1,l,r-1)<<endl;
else cout<<tr.query(1,l,r-1)-tr.query(1,l-1,l-1)-a[l]<<endl;
}
}
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int _=1;
cin>>_;
while(_--)solve();
return 0;
}