心得
[ICPC2024 Xi'an I] ICPC2024 邀请赛西安站重现赛 - 比赛详情 - 洛谷
7表示赛时ac了7个,8表示含补题总共ac数,13表示题目总数
题目
M. Chained Lights
打表,发现只有k=1是YES
cpp
//#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=1e6+10;
int t,n,k,fac[N],sum[N],light[N];
void press(int x)
{
light[x]^=1;
for (int y=x+x;y<=n;y+=x) press(y);
}
int main(){
sci(t);
while(t--){
sci(n),sci(k);
puts(k==1?"YES":"NO");
}
return 0;
}
J. Triangle
数三角形,手玩发现一些规律,
比如:n=3,m=9实际是15,然后发现和gcd有关
cpp
//#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=1e6+10;
int a,b;
ll gcd(ll a,ll b){
return !b?a:gcd(b,a%b);
}
int main(){
sci(a),sci(b);
// if(a==b){
// printf("%lld\n",1ll*a*(a+1)/2);
// }
// else{
ll v=gcd(a,b);
ptlle((1ll*a*b-1ll*v*(a/v)*(b/v))/2+1ll*((a/v)*(b/v)+1)/2*v);
//}
return 0;
}
/*
3 9 = 15
*/
D. Make Them Straight
枚举k,根据ai-i*k确定能在同一个序列里的子序列,子序列里的是不改的
首项得是正的,后面i*k>1e6说明一定要改
剪枝一下复杂度是可接受的O(klogn)
cpp
//#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=2e5+10;
int n,a[N],b[N];
map<ll,ll>mp;
ll sum,ans;
int main(){
sci(n);
rep(i,0,n-1)sci(a[i]);
rep(i,0,n-1){
sci(b[i]);
sum+=b[i];
}
ans=sum;
rep(k,0,1000000){
mp.clear();
ll res=0;
rep(i,0,n-1){
ll v=a[i]-1ll*i*k;
if(1ll*i*k>1000000)break;
if(v<0)continue;
mp[v]+=b[i];
res=max(res,mp[v]);
//if(mp[v]==9)printf("i:%d v:%lld mp:%lld\n",i,v,mp[v]);
}
ans=min(ans,sum-res);
}
ptlle(ans);
return 0;
}
/*
3 9 = 15
*/
L. Rubbish Sorting
二进制子集枚举一下,大概sosdp的思想,因为|s|<=5
cpp
#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=2e5+10,M=2e7+10,INF=0x3f3f3f3f;
int q,n,op,x,y,bs[8];
char s[N];
int val[M],now=INF;
int main(){
memset(val,INF,sizeof val);
bs[0]=1;
rep(i,1,6)bs[i]=bs[i-1]*28;
sci(q);
while(q--){
sci(op);
scanf("%s",s);
n=strlen(s);
if(op==1){
sci(y);
int w=0;
rep(i,0,n-1){
int v=s[i]-'a'+1;
w+=v*bs[i];
}
val[w]=min(val[w],y);
now=min(now,y);
rep(p,1,n){
int up=(1<<p)-1;
rep(i,0,up-1){
int w=0;
rep(j,0,p-1){
int z=i>>j&1,v=0;
if(z)v=27;
else v=(s[j]-'a'+1);
w+=v*bs[j];
}
val[w]=min(val[w],y);
}
}
}
else{
int w=0;
rep(i,0,n-1){
int v=s[i]-'a'+1;
w+=v*bs[i];
}
if(val[w]!=INF){
pte(val[w]);
continue;
}
vector<int>mn(n+1,INF);
mn[n]=now;
rep(p,1,n){
int up=(1<<p)-1;
rep(i,0,up-1){
int w=0,tot=0;
rep(j,0,p-1){
int z=i>>j&1,v=0;
if(z)v=27,tot++;
else v=(s[j]-'a'+1);
w+=v*bs[j];
}
mn[tot+n-p]=min(mn[tot+n-p],val[w]);
}
}
rep(i,0,n){
if(mn[i]!=INF){
//printf("i:%d mn:%d\n",i,mn[i]);
pte(mn[i]);
break;
}
}
}
}
return 0;
}
/*
3 9 = 15
*/
B. Turn Off The Lights(构造)
最终一定是和某一列完全相同/完全相反的,
不妨和第i列完全相同,全是01011,那么再把这三列的1按列取消掉就可以了
所以枚举和哪列相同,bitset加速统计,复杂度O(n^3/64)
cpp
#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=1e3+10;
int n,k,a[N][N],sum[N];
bitset<1005>b[2][N];
bool flip[N];
bool ck(int i){
int sum=0;
rep(j,1,n){
if(j==i)continue;
int s1=(b[1][i]^b[0][j]).count(),s2=(b[0][i]^b[0][j]).count();
if(s1<s2)flip[j]=1;
else flip[j]=0;
sum+=min(s1,s2);
}
return sum<=k;
}
void out(int i){
//printf("i:%d\n",i);
vector<P>ans;
rep(j,1,n){
if(j==i)continue;
if(flip[j])ans.pb(P(j,0));
rep(x,1,n){
if(flip[j]){
if(a[j][x]!=(a[i][x]^1))ans.pb(P(j,x));
}
else{
if(a[j][x]!=a[i][x])ans.pb(P(j,x));
}
}
}
rep(x,1,n){
if(a[i][x])ans.pb(P(0,x));
}
pte(SZ(ans));
for(auto x:ans){
printf("%d %d\n",x.fi,x.se);
}
}
int main(){
sci(n),sci(k);
rep(i,1,n){
rep(j,1,n){
sci(a[i][j]);
if(a[i][j])b[0][i].set(j);
else b[1][i].set(j);
}
}
rep(i,1,n){
rep(x,0,1){
if(ck(i)){
out(i);
return 0;
}
}
}
puts("-1");
return 0;
}
/*
3 9 = 15
*/
F. XOR Game(博弈)
先从大到小考虑ai=1的值,z是0的个数算最小的数
因为操作一次就可以获得收益/ban掉对应收益,所以alice和bob会先抢这部分
剩下的局面,尽可能避免2的出现, 全是2的情况,谁先操作谁就输了,
所以判一下剩下局面的奇偶性,奇数alice全取,偶数bob全ban
cpp
#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=1e5+10;
int k,z,a[N],b[N],c;
int main(){
sci(k);sci(z);
rep(i,0,k-1){
sci(a[i]);
}
per(i,k-1,0){
if(a[i]==1){
c++;
if(c&1)b[i]=1;
a[i]=0;
}
}
per(i,k-1,0){
if(a[i]&1)c++;
}
c+=(z&1);
per(i,k-1,0){
if(!a[i])continue;
if(c&1)b[i]=1;
}
per(i,k-1,0){
printf("%1d",b[i]);
}
puts("");
return 0;
}
/*
3 9 = 15
*/
I. Smart Quality Inspector(状压dp+区间dp)
避免后效性,肯定是从大到小考虑max值,
被前面的最大值覆盖了的区间,再选最大值时就没有贡献了
dp[S]表示当前最大值已经覆盖的状态是S时的最大代价和
b[i][l][r]表示区间包含第i位且被[l,r]完整包含的区间的数量
新增一位时,往左往右拓展0的区域找到最左最右,这一段的b值对应的区间都是有贡献的
复杂度O(n^5+n*2^n)
cpp
#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=25,M=(1<<20)+5,INF=0x3f3f3f3f;
int n,k,m,l,r,a[N][N],b[N][N][N],dp[M];
void upd(int &x,int y){
x=min(x,y);
}
int main(){
sci(n),sci(k),sci(m);
rep(i,1,m){
sci(l),sci(r);
a[l][r]++;
}
rep(i,1,n){
rep(l,1,i){
rep(r,i,n){
rep(x,l,i){
rep(y,i,r){
b[i][l][r]+=a[x][y];
}
}
}
}
}
memset(dp,INF,sizeof dp);
dp[0]=0;
int up=(1<<n)-1;
rep(i,0,up){
vector<int>pre(n,-1),suf(n,n);
int now=-1,bit=0;
rep(j,0,n-1){
int v=i>>j&1;
if(v==1)now=j,bit++;
else pre[j]=now+1;
}
now=n;
per(j,n-1,0){
int v=i>>j&1;
if(v==1)now=j;
else{
suf[j]=now-1;
int x=j+1,y=pre[j]+1,z=suf[j]+1;
upd(dp[i|(1<<j)],dp[i]+b[x][y][z]*max(k-bit,0));
}
}
}
pte(dp[up]);
return 0;
}
赛后补题
G. The Last Cumulonimbus Cloud(拓扑排序好题)
任意非空子图都有一个度不超过10的点=可以把度>10的点的贡献都归到这些不超过10的点上
拓扑排序每删掉一个度数不足10的点就会多出一个不足10的点
最后图可以化成一个联通且每个点出度均不超过10的dag
u加的时候把贡献推到u的下游
u算的时候上游已经推给过u,只需要再加上u的所有下游的贡献