ZJNU 2021-07-14 个人排位赛3 部分题解
完全不会数据结构就比较离谱
A - Roadblock
题意
FJ的农场由\(N\)个点\(M\)条边组成,边存在边权
它每次都会沿着最短路径从点\(1\)走到点\(N\)
现在FJ的牛想选择这张图中的任意一条边,使其边权翻倍
问选择某条边翻倍后,FJ需要多走的距离的最大值是多少(最短路的最大增量)
标签
Dijkstra
SPFA
思路
先在原图中跑一遍SPFA,求出此时的最短路长度\(originalDistance\)并且回溯得到这条路的每一条边
因为只有把最短路上的边翻倍,FJ才有可能走更长的路
所以枚举这条路上的每一条边,翻倍后尝试再跑一遍最短路(这里推荐用Dijkstra),求出此时的最短路长度\(currentDistance\)
答案就是\(currentDistance-originalDistance\)的最大值
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
int n,m;
vector<P> G[105];
int dis[105];
int pre[105];
vector<P> edge;
int curu,curv;
void spfa()
{
queue<int> q;
mst(dis,INF);
dis[1]=0;
q.push(1);
while(!q.empty())
{
int u=q.front();
q.pop();
for(P pd:G[u])
{
int v=pd.first,w=pd.second;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push(v);
pre[v]=u;
}
}
}
int pos=n;
while(pos!=1)
{
edge.pb(P(pos,pre[pos]));
pos=pre[pos];
}
}
void dijkstra()
{
priority_queue<P> pq;
mst(dis,INF);
dis[1]=0;
pq.push(P(0,1));
while(!pq.empty())
{
P pp=pq.top();
pq.pop();
int u=pp.second;
if(dis[u]!=-pp.first)
continue;
for(P pd:G[u])
{
int v=pd.first,w=pd.second;
if(u==curu&&v==curv||u==curv&&v==curu)
w<<=1;
if(dis[u]+w<dis[v])
{
dis[v]=dis[u]+w;
pq.push(P(-dis[v],v));
}
}
}
}
void solve()
{
cin>>n>>m;
rep(i,1,m)
{
int a,b,c;
cin>>a>>b>>c;
G[a].pb(P(b,c));
G[b].pb(P(a,c));
}
spfa();
int curans=dis[n];
int maxans=0;
for(P p:edge)
{
curu=p.first;
curv=p.second;
dijkstra();
maxans=max(maxans,dis[n]);
}
cout<<maxans-curans<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
B - Grass Planting
题意
FJ有张\(N\)个点\(N-1\)条边的树图
初始时每条边的边权为\(0\)
有两种操作:
\(P\)操作会把\(u\)到\(v\)的最短路径上的每条边边权\(+1\)
\(Q\)操作则会询问某条边的边权(保证询问的两个点是相邻的)
标签
Segment Tree
LCA
DFS Clock
思路
暂无,等想好了再改
或者建议直接问余总
代码
//#include<ext/pb_ds/assoc_container.hpp>
//#include<ext/pb_ds/hash_policy.hpp>
#include<bits/stdc++.h>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
#define all(a) (a).begin(),(a).end()
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
using namespace std;
//using namespace __gnu_pbds;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-12;
const double PI=acos(-1.0);
const ll mod=998244353;
const int dx[8]={0,1,0,-1,1,1,-1,-1},dy[8]={1,0,-1,0,1,-1,1,-1};
mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}
int n;
const int MAXN=100050,MAXF=18;
vector<int> G[MAXN];
int depth[MAXN];
int father[MAXN][MAXF];
bool vis[MAXN];
int lll[MAXN],rrr[MAXN],sz[MAXN];
int clk=0;
void dfs(int pos,int fa)
{
vis[pos]=true;
depth[pos]=depth[fa]+1;
father[pos][0]=fa;
for(int i=1;i<MAXF;i++)
father[pos][i]=father[father[pos][i-1]][i-1];
int cnt=G[pos].size();
lll[pos]=++clk;
sz[pos]=1;
for(int i=0;i<cnt;i++)
{
if(!vis[G[pos][i]])
{
dfs(G[pos][i],pos);
sz[pos]+=sz[G[pos][i]];
}
}
rrr[pos]=lll[pos]+sz[pos]-1;
}
int lca(int a,int b)
{
while(depth[a]<depth[b])
{
for(int i=MAXF-1;i>=0;i--)
{
if(depth[b]-(1<<i)>=depth[a])
b=father[b][i];
}
}
while(depth[a]>depth[b])
{
for(int i=MAXF-1;i>=0;i--)
{
if(depth[a]-(1<<i)>=depth[b])
a=father[a][i];
}
}
if(a==b)
return a;
while(father[a][0]!=father[b][0])
{
for(int i=MAXF-1;i>=0;i--)
{
if(father[a][i]!=father[b][i])
{
a=father[a][i];
b=father[b][i];
}
}
}
return father[a][0];
}
struct node
{
int l,r;
ll sum,lazy;
}tree[MAXN*4];
void push_up(int id)
{
tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
}
void push_down(int id)
{
if(tree[id].lazy)
{
int m=tree[id].r-tree[id].l+1;
tree[id<<1].lazy+=tree[id].lazy;
tree[id<<1|1].lazy+=tree[id].lazy;
tree[id<<1].sum+=tree[id].lazy*(m-(m>>1));
tree[id<<1|1].sum+=tree[id].lazy*(m>>1);
tree[id].lazy=0;
}
}
void buildTree(int id,int l,int r)
{
tree[id].l=l;
tree[id].r=r;
tree[id].lazy=0;
if(l==r)
{
tree[id].sum=0;
return;
}
int mid=(l+r)>>1;
buildTree(id<<1,l,mid);
buildTree(id<<1|1,mid+1,r);
push_up(id);
}
void update(int id,int L,int R,ll val)
{
if(L<=tree[id].l&&R>=tree[id].r)
{
tree[id].sum+=val*(tree[id].r-tree[id].l+1);
tree[id].lazy+=val;
return;
}
push_down(id);
int mid=(tree[id].r+tree[id].l)>>1;
if(L<=mid)
update(id<<1,L,R,val);
if(R>mid)
update(id<<1|1,L,R,val);
push_up(id);
}
ll query_sum(int id,int L,int R)
{
if(L<=tree[id].l&&R>=tree[id].r)
return tree[id].sum;
push_down(id);
int mid=(tree[id].r+tree[id].l)>>1;
ll ans=0;
if(L<=mid)
ans+=query_sum(id<<1,L,R);
if(R>mid)
ans+=query_sum(id<<1|1,L,R);
return ans;
}
void solve()
{
int q;
cin>>n>>q;
repp(i,1,n)
{
int a,b;
cin>>a>>b;
G[a].pb(b);
G[b].pb(a);
}
dfs(1,0);
buildTree(1,1,n);
while(q--)
{
string op;
cin>>op;
if(op=="P")
{
int u,v;
cin>>u>>v;
int z=lca(u,v);
update(1,lll[u],lll[u],1);
update(1,lll[v],lll[v],1);
update(1,lll[z],lll[z],-2);
}
else
{
int u,v;
cin>>u>>v;
if(depth[u]<depth[v])
swap(u,v);
cout<<query_sum(1,lll[u],rrr[u])<<'\n';
}
}
}
int main()
{
closeSync;
//multiCase
{
solve();
}
return 0;
}
C - Hay Bales
标签
Implementation
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
int a[10010];
void solve()
{
int n,s=0;
cin>>n;
rep(i,1,n)
{
cin>>a[i];
s+=a[i];
}
s/=n;
int r=0;
rep(i,1,n)
r+=max(a[i]-s,0);
cout<<r<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
D - Cow Photography II
E - Cow Photography I
题意
两道题大致意思相同,直接讲Hard版本
FJ会按某一种顺序让\(N\)头牛排成一队照相
但每次在照相前,总会有一些牛会走出队列,然后随意插入队列中的某些位置
FJ就这样拍了\(5\)张照,每次拍照后都会再让所有牛按原先安排的顺序排回去
现在只知道,对于每一头牛,在这\(5\)张照片中它只会主动走到其他位置一次,剩余四张在拍照时不会主动移动(也可能\(5\)张照片中都不走动)
试根据这\(5\)张照片判断出原先安排的顺序是什么
标签
Sorting
(Hard Version) Hashing
思路
首先抓住题目的要点,也就是\(5\)张照片中每头牛最多主动走到其他位置一次
也就是说,对于任意的两头牛\(a,b\),一开始\(a\)排在\(b\)前面
某一次照相中\(a\)主动走到\(b\)后面
另一次照相中\(b\)主动走到\(a\)前面
这两次照相\(a,b\)的顺序和原先是不同的
但是其他三次照相都是按照原先顺序不会改变
所以可以通过牛的\(id\)反标记其原来的位置,可以利用Hash方法(map/unordered_map/gp_hash_table等)来记录
- 利用\(pos[i][j]\)表示在第\(i\)次照相中,id为\(j\)的牛的位置
那么在确定最终的顺序时,如果想要判断任意两头牛\(a,b\)的顺序是怎样的,只需要检查在\(5\)次照相中,\(a\)排在\(b\)前面的次数多还是反之
但是由于\(N=20000\),无法根据拓扑排序建图来获得答案
但可以根据快速排序\(O(n\log n)\)的时间复杂度,重写id的判定规则来实现
总时间复杂度为\(O(n\log n\log n)\),使用unordered_map或gp_hash_table时可以优化成常数较大的\(O(n\log n)\)
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
int n;
int a[5][20010];
unordered_map<int,int> pos[5];
struct node
{
int id;
bool operator < (const node& a) const
{
int l=0,r=0;
repp(t,0,5)
{
if(pos[t][id]<pos[t][a.id])
l++;
else
r++;
}
return l>r;
}
}ar[20010];
void solve()
{
cin>>n;
repp(t,0,5)
{
rep(i,1,n)
{
cin>>a[t][i];
pos[t][a[t][i]]=i; //id反记录位置
}
}
rep(i,1,n)
ar[i].id=a[0][i];
sort(ar+1,ar+n+1);
rep(i,1,n)
cout<<ar[i].id<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
F - Simplifying the Farm
题意
\(N\)个点\(M\)条边的图
要求求出最小生成树的路径权值和,以及不同的最小生成树的种类数
已知同样长度的边最多不超过\(3\)条
标签
Minimum Spanning Tree : Kruskal
Counting & Implementation
思路
(是最暴力的分类讨论实现)
最小生成树计数的矩阵树原理不适用于本题中这样的大数据
但题目给出了一条关键限制——同样长度的边最多不超过\(3\)条
并且数据范围中每条边的长度限制在\(10^6\)以内
所以可以开一个\(10^6\)大小的 vector<int> eg[i] 来记录长度为\(i\)的边有哪些
根据最小生成树的\(Kruskal\)算法,将边按照从小到大排序后,如果某条边两端的点处于不同的集合内,就将其加入最小生成树中,贡献是将图中独立的集合\(-1\)
可以从\(1\)到\(10^6\)枚举长度,忽略两端点已经在同一个集合内的边,将剩余的边取出进行分类讨论
①
如果相同长度的可取的边有且仅有一条,那么这条边必取
合并这条边的两端点集合
最小生成树总长度\(+i\)(\(i\)即当前枚举到的边的长度)
总种类数不变
②
如果相同长度的可取的边有两条
画图,得以下三种情况(图中每个小圆圈代表独立的集合)
对于第一种情况(左一),两条边任意取一条即可
此时最小生成树总长度\(+i\)即可
种类数也就是\(C_2^1=2\),让总种类数乘上\(2\)
对于其余情况(左二、左三),在Kruskal算法中每条边都是需要取的,都会使得图中的独立集合数\(-1\)
此时最小生成树总长度\(+2i\)
总种类数不变
③
如果相同长度的可取的边有三条
画图,得以下八种情况(蓝色数字表示涉及到的集合数量)
按涉及到的集合数量进行讨论:
- 集合数量为\(2\),仅一种情况(上一),此时三条边任选其一即可
- 总长度加\(i\)
- 种类数乘\(C_3^1=3\)
- 集合数量为\(3\),有两种情况(上二、下四)
- 对于上二,重复的两条边任选其一,第三条边必选
- 总长度加\(2i\)
- 种类数乘\(C_2^1=2\)
- 对于下四,三条边任选两条便可保证图联通
- 总长度加\(2i\)
- 种类数乘\(C_3^2=3\)
- 对于上二,重复的两条边任选其一,第三条边必选
- 集合数量为\(4\),有三种情况(上三、下一、下二)
- 对于上三,重复的两条边任选其一,第三条边必选
- 总长度加\(2i\)
- 种类数乘\(C_2^1=2\)
- 对于下一和下二,所有边都是必选边
- 总长度加\(3i\)
- 种类数不变
- 对于上三,重复的两条边任选其一,第三条边必选
- 集合数量为\(5\)或者\(6\)时,所有边都是必选的
- 总长度加\(3i\)
- 种类数不变
最后,根据上述各图的特性,综合\(vector/set\)等容器来判断即可
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
int n,m;
vector<P> eg[1000050];
int gp[40050];
int fnd(int p)
{
return p==gp[p]?p:(gp[p]=fnd(gp[p]));
}
void merge(int a,int b)
{
gp[fnd(b)]=fnd(a);
}
void merge(P pd)
{
merge(pd.first,pd.second);
}
void kruskal()
{
int tmp=1;
ll mincost=0,kinds=1;
rep(i,1,1000000)
{
if(!eg[i].empty())
{
vector<P> vec;
for(P &pd:eg[i])
{
int uu=fnd(pd.first),vv=fnd(pd.second);
if(uu==vv)
continue; //集合已经相同的话不纳入选择范围
if(uu>vv)
swap(uu,vv); //小在前大在后,方便判断
vec.pb(P(uu,vv));
}
sort(vec.begin(),vec.end());
if(vec.size()==1) //相同长度的(可选)边只有一条时
{
mincost+=i;
merge(vec[0]);
}
else if(vec.size()==2) //相同长度的(可选)边有两条时
{
set<int> st; //涉及到的集合数量
for(P pd:vec)
{
st.insert(pd.first);
st.insert(pd.second);
}
if(st.size()==2)
{
mincost+=i;
kinds=(kinds*2)%mod;
merge(vec[0]);
}
else
{
mincost+=i*2;
merge(vec[0]);
merge(vec[1]);
}
}
else if(vec.size()==3) //相同长度的(可选)边有三条时
{
set<int> st;
for(P pd:vec)
{
st.insert(pd.first);
st.insert(pd.second);
}
if(st.size()==2)
{
mincost+=i;
kinds=(kinds*3)%mod;
merge(vec[0]);
}
else if(st.size()==3)
{
if(vec[0]==vec[1]||vec[1]==vec[2]) //存在两条边是相同的
{
if(vec[0]==vec[1])
{
mincost+=i*2;
kinds=(kinds*2)%mod;
merge(vec[0]);
merge(vec[2]);
}
else
{
mincost+=i*2;
kinds=(kinds*2)%mod;
merge(vec[0]);
merge(vec[1]);
}
}
else
{
mincost+=i*2;
kinds=(kinds*3)%mod;
merge(vec[0]);
merge(vec[1]);
}
}
else if(st.size()==4)
{
if(vec[0]==vec[1]||vec[1]==vec[2])
{
if(vec[0]==vec[1])
{
mincost+=i*2;
kinds=(kinds*2)%mod;
merge(vec[0]);
merge(vec[2]);
}
else
{
mincost+=i*2;
kinds=(kinds*2)%mod;
merge(vec[0]);
merge(vec[1]);
}
}
else
{
mincost+=i*3;
merge(vec[0]);
merge(vec[1]);
merge(vec[2]);
}
}
else if(st.size()>=5)
{
mincost+=i*3;
merge(vec[0]);
merge(vec[1]);
merge(vec[2]);
}
}
}
}
//mincost%=mod;
cout<<mincost<<' '<<kinds<<'\n';
}
void solve()
{
cin>>n>>m;
if(n==1)
{
cout<<"0 1\n";
return;
}
rep(i,1,m)
{
int u,v,w;
cin>>u>>v>>w;
eg[w].pb(P(u,v));
}
rep(i,1,n)
gp[i]=i;
kruskal();
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
G - Umbrellas for Cows
题意
\(N\)头牛分散在某一长度为\(M\)的数轴上的某些点
已知买一把宽度为\(i\)的雨伞需要花费\(C_i\)
如果要将位置为\(a,b\)的两头牛用一把雨伞盖住,则雨伞的宽度至少要为\(b-a+1\)
问将所有牛盖住的最小花费是多少
在最优方案中,雨伞是允许重叠的
标签
Dynamic Programming
思路
明显的\(O(n^2)\)的\(DP\)
因为雨伞并不是说随着宽度\(i\)增加,\(C_i\)也一定会增加的,并且题目允许雨伞重叠
所以可以将宽度较小的雨伞与比其宽度大的雨伞中的最小花费取小,使得\(C_i\)最后呈一个递增的顺序
于是便可以进行\(DP\)转移,转移方程为
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
int n,m;
int p[5050];
ll c[100050];
ll dp[5050];
void solve()
{
cin>>n>>m;
rep(i,1,n)
cin>>p[i];
rep(i,1,m)
cin>>c[i];
sort(p+1,p+n+1);
per(i,m-1,1)
c[i]=min(c[i],c[i+1]);
//rep(i,1,m) cerr<<"? "<<i<<' '<<c[i]<<'\n';
dp[0]=0;
rep(i,1,n)
{
dp[i]=LINF;
repp(j,0,i)
dp[i]=min(dp[i],dp[j]+c[p[i]-p[j+1]+1]);
}
cout<<dp[n]<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
H - Routing Schemes
防AK 😦
I - SegmentTree!
标签
Segment Tree
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const int MAXN=1e5+50;
ll a[MAXN];
struct node
{
int l,r;
ll lazy,maxn;
}tree[MAXN<<2];
void push_up(int id)
{
tree[id].maxn=max(tree[id<<1].maxn,tree[id<<1|1].maxn);
}
void push_down(int id)
{
if(tree[id].lazy)
{
int m=tree[id].r-tree[id].l+1;
tree[id<<1].lazy+=tree[id].lazy;
tree[id<<1|1].lazy+=tree[id].lazy;
tree[id<<1].maxn+=tree[id].lazy;
tree[id<<1|1].maxn+=tree[id].lazy;
tree[id].lazy=0;
}
}
void buildTree(int id,int l,int r)
{
tree[id].l=l;
tree[id].r=r;
tree[id].lazy=0;
if(l==r)
{
tree[id].maxn=a[l];
return;
}
int mid=(l+r)>>1;
buildTree(id<<1,l,mid);
buildTree(id<<1|1,mid+1,r);
push_up(id);
}
void update(int id,int L,int R,ll val)
{
if(L<=tree[id].l&&R>=tree[id].r)
{
tree[id].maxn+=val;
tree[id].lazy+=val;
return;
}
push_down(id);
int mid=(tree[id].r+tree[id].l)>>1;
if(L<=mid)
update(id<<1,L,R,val);
if(R>mid)
update(id<<1|1,L,R,val);
push_up(id);
}
ll query_max(int id,int L,int R)
{
if(L<=tree[id].l&&R>=tree[id].r)
return tree[id].maxn;
push_down(id);
int mid=(tree[id].r+tree[id].l)>>1;
ll ans=-LINF;
if(L<=mid)
ans=max(ans,query_max(id<<1,L,R));
if(R>mid)
ans=max(ans,query_max(id<<1|1,L,R));
return ans;
}
int n,m;
void solve()
{
cin>>n>>m;
rep(i,1,n)
cin>>a[i];
buildTree(1,1,n);
while(m--)
{
int op;
cin>>op;
if(op==1)
{
int p; ll v;
cin>>p>>v;
update(1,p,p,v-a[p]);
a[p]=v;
}
else
{
int l,r;
cin>>l>>r;
cout<<query_max(1,l,r)<<'\n';
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
J - Escaping the Farm
题意
有\(N\)个数,要求选择一些数,使得按照十进制相加时不会产生进位
问最多能选择的数的数量
标签
Brute Force
思路
由于\(N\)最大只有\(20\),直接\(O(2^{20})\)枚举出所有状态,模拟加法判断进位即可
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
int n,a[22][11];
bool check(int stat)
{
rep(j,0,9)
{
int t=0;
rep(i,1,n)
{
if(stat>>(i-1)&1)
t+=a[i][j];
}
if(t>9)
return false;
}
return true;
}
void solve()
{
cin>>n;
rep(i,1,n)
{
int d;
cin>>d;
int p=0;
while(d)
{
a[i][p]=d%10;
d/=10;
p++;
}
}
int r=0;
repp(t,1,1<<n)
{
if(check(t))
r=max(r,(int)__builtin_popcount(t));
}
cout<<r<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}
K - Counting Graphs
防AK 😦