2022.6.17练题
七点十分(起晚了),补一套800 div2
A
实际上就是交替输出,因为可证在a与b个数不相同时,交替是使creepiness min的情况。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int t; cin>>t; while(t--){ int a,b; cin>>a>>b; if(a>=2*b){ for(int i=1;i<=(a-1)/2;i++) cout<<"0"; for(int i=1;i<=b;i++) cout<<"1"; for(int i=1;i<=a-(a-1)/2;i++) cout<<"0"; cout<<"\n"; } else if(a>=b&&a<2*b){ for(int i=1;i<=b;i++) cout<<"01"; for(int i=1;i<=a-b;i++) cout<<"0"; cout<<"\n"; } else if(b>=2*a){ for(int i=1;i<=(b-1)/2;i++) cout<<"1"; for(int i=1;i<=a;i++) cout<<"0"; for(int i=1;i<=b-(b-1)/2;i++) cout<<"1"; cout<<"\n"; } else if(b<2*a&&b>=a){ for(int i=1;i<=a;i++) cout<<"10"; for(int i=1;i<=b-a;i++) cout<<"1"; cout<<"\n"; } } return 0; }
B(构造,不是贪心,constructive algorithms)
用全部的串的数量减去变形后有11或00或者没有变形含有11或00的串的数量
记得开long long
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int t; cin>>t; while(t--){ ll n; cin>>n; char s[n+1]; for(int i=1;i<=n;i++) cin>>s[i]; ll cnt=0; for(int i=1;i<n;i++){ if(s[i]=='0'&&s[i+1]=='0') cnt+=i; else if(s[i]=='1'&&s[i+1]=='1') cnt+=i; } cout<<(n+1)*n/2-cnt<<"\n"; } return 0; }
C(找规律,前缀和)
有一个长度为n的数组a,开始全是0,指针在第一个元素处。
我们可以以任意顺序操作这两个步骤:
1.指针不在最后一个位置,就使指针现在所在的元素+1,指针向后移一位;
2.指针不在第一个位置,就使指针现在所在的元素-1,指针向前移一位;
在我们操作完了以后,指针必须在第一个元素上。
给你数组a,判断是否能通过操作得到数组a
三个规律:
pre[n]!=0 NO
pre[i]<0 NO
pre[i]=0以后还有非零元素 NO
记得开 long long
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+30; const int inf=1e9+7; int a[maxn]; ll pre[maxn]; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int t; cin>>t; while(t--){ int n; cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; pre[i]=pre[i-1]+a[i]; } if(pre[n]!=0){ cout<<"NO"<<"\n"; continue;//总和!=0 } bool ok=true,pos=false; for(int i=1;i<=n;i++){ if(pre[i]<0) ok=false;//有前缀和<0的情况 } for(int i=1;i<=n;i++){ if(pre[i]==0) pos=true; else if(pos) ok=false;//前缀和=0后又!=0,说明在前缀和=0后面的位置出现了非0元素 } if(ok) cout<<"YES"<<"\n"; else cout<<"NO"<<"\n"; } return 0; }
D E(图论)
D:
给一个有根树,顶点1~n,树根顶点是1,v的父节点是Pv。在每个点上有一个数字,一开始全是0,让我们将写在v点上的数字叫av。对于每个v来说,我们想要av在lv和rv之间
在单独一步操作中,我们做以下事情:
1.选一些顶点v。让b1~bk成为从顶点1到顶点v的边长。(b1=1,bk=v,bi=p*bi+1)
2.选一个不减的数组c带着k个非负数
3.对于每个i,用ci加上abi
要想完成目标,最少操作数是多少?
输入
t个样例
n个顶点
p2~pn(i的父节点)
n行li,ri
输出
最小操作步数
思路:从根节点开始dfs,用二维vector记下父节点信息,当sum加过一遍还<(ll)l[v]时ans++即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef double db; const int maxn=2e5+30; int l[maxn],r[maxn]; int t,n,ans; vector<int> vec[maxn]; ll dfs(int v){ ll sum=0; for(auto u:vec[v]){ sum+=dfs(u); } if(sum<(ll)l[v]){ ans++;//小于需要操作 return r[v]; } return min(ll(r[v]),sum); } int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); cin>>t; while(t--){ cin>>n;//节点数量 for(int i=2;i<=n;i++){ int p; cin>>p; vec[p].push_back(i);//记录父节点,便于dfs } for(int i=1;i<=n;i++) cin>>l[i]>>r[i]; ans=0; dfs(1);//根节点开始 cout<<ans<<"\n"; for(int i=1;i<=n;i++) vec[i].clear(); } return 0; }
E
A从伊朗前往意大利参加TY音乐会。意大利有n个编号从1~n的城市和编号从1~m的m条路。最初,K在城市1,想去城市nA的家里玩。因为K不知道意大利的地图,A帮助他来尽快看到他们两个。
A发给K信息:
A把K不能用的路发给K,K会在当天停留在所处的城市。
A告诉K要移动。之后,K会随机的选择一个可到达的城市,并去到那里。
A知道K的现在所处的地点。
A和K想知道最小的可能整数d,使得经过d天,他们确保能看到对方。
输入
n(城市数量) m(路的数量)
vi ui 说明vi和ui联通
输出
d
变种dijkstra
不会解释d[v] d[u]……………………(QAQ)
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define fi first #define se second const int maxn=2e5+30; int n,m,d[maxn],dis[maxn]; priority_queue<pair<int,int> > pq; vector<int> g[maxn];//存图 bool vis[maxn]; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); cin>>n>>m; fill(dis,dis+maxn,m); for(int i=1;i<=m;i++){ int v,u; cin>>v>>u; g[u].push_back(v);//存图 d[v]++;//block时间 } dis[n]=0; pq.push({0,n}); while(!pq.empty()){ int v=pq.top().se; pq.pop(); if(vis[v]) continue; vis[v]=true;//来过 for(int u:g[v]){ if(dis[v]+d[u]<dis[u]){ dis[u]=dis[v]+d[u]; pq.push({-dis[u],u}); } --d[u]; } } cout<<dis[1]<<"\n";//从1开始 return 0; }
下午13:30-18:30,VP了一套2020-2021 ACM-ICPC, Asia Seoul Regional Contest。(知识点严重不足)
链接:https://codeforces.com/gym/386114
A:看了图以后迅速跳过
B:简单概率题
纪念骰子(签到)
ICPC从2000年每年开始举办。为了纪念,做了个骰子。六个面的数字之和是21即可。赢的人是掷的点数
多的那个人。简单概率,看样例就能写代码,注意约分。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=10; int a[maxn],b[maxn]; int main(){ int ans1=0,ans2=36; for(int i=1;i<=6;i++) scanf("%d",&a[i]); for(int i=1;i<=6;i++) scanf("%d",&b[i]); for(int i=1;i<=6;i++){ for(int j=1;j<=6;j++){ if(b[j]<a[i]) ans1++; } } for(int i=2;i<=ans1;i++){ while(ans2%i==0&&ans1%i==0) ans1/=i,ans2/=i; } printf("%d/%d",ans1,ans2); printf("\n"); return 0; }
C:(图论标准假题,和边权两个值毫无关系)
开始经营生意的K准备在毕业后开一家甜点店。K住的地方是一个树形结构,有n个预定的位置给甜品店。在下图中,一个圆圈代表着一个甜品店的预留位置,在线上的值代表着路的长度。
有K座公寓综合楼,所以他想让他的甜品店离公寓楼越近越好。
输入
n k
n-1行,u,v,w(表示点u到点v距离为w)
最后一行 k个公寓点
分析:只要是处于两个特殊节点路径上的节点都符合题目的要求,那么就是爆搜整棵树即可,每次抓一个最深的点,大力跳fa,用set来记录点,避免重复。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e6+30; int head[maxn]; int n,k,cnt; bool vis[maxn]; set<int> s; struct Edge{ int next,to,dis; }edge[maxn]; void addedge(int from,int to,int dis){ edge[cnt].next=head[from]; edge[cnt].to=to; edge[cnt].dis=dis; head[from]=cnt++; } bool dfs(int u,int fa){ bool fg=false; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(v==fa) continue; fg|=dfs(v,u); } if(fg||vis[u]){ s.insert(u); return true; } else return false; } int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); cin>>n>>k; memset(head,-1,sizeof(head)); for(int i=1;i<=n-1;i++){ int u,v,w; cin>>u>>v>>w; addedge(u,v,w); addedge(v,u,w);//无向图 } int x=0; for(int i=1;i<=k;i++){ cin>>x; vis[x]=true; } dfs(x,0); cout<<(int)s.size()<<"\n"; return 0; }
D:(复杂DP转移)
有空挑战一下
E:不精确的电脑(思维)
IC是一台电脑,当两个整数相差2时,它才能比较正确。现对于长度为n的数列 Pn={1,2,3,…,n} 中每个数依次与别的数进行比较,若大于得一分,比赛进行两轮,让你判断输入的数组Dn是否为两场比赛中每个数可能出现的分差。
最终问题化为:
我们从前往后扫一遍
如果是0的话,不需要操作
如果是1的话,可以和前面的1一起消为0
如果是2的话,改成向前传递一个1,向后传递一个1,自己变成0
由此可以得出简化之后的做法:
我们现在有两个变量a,b,初始值为0。从前往后扫一遍,如果遇到1,选取a或者b XOR 1(如果能消为0,优先)。如果遇到2,由于2必须往前面传递一个1,所以要求此时a XOR b 必须为1。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e6+30; int m[maxn]; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int n; cin>>n; int a,b; for(int i=1;i<=n;i++) cin>>m[i]; for(int i=1;i<=n;i++){ if(m[i]==1){ if(a==1) a^=1; else if(b==1) b^=1; else a^=1; } else if(m[i]==2){ if(a^b!=1){ cout<<"NO"<<"\n"; return 0; } } } if(a==0&&b==0) cout<<"YES"<<"\n"; else cout<<"NO"<<"\n"; return 0; }
F
题目长度和图直接把我劝退,有空看看
G(等差数列,评测机感觉有问题)
移动机器人(数学)
移动机器人现在经常被用到。你负责控制n个移动机器人,它们探索一个很长、很窄、很直的洞穴,这个洞穴就像一条线。移动机器人从附近的环境收集数据,并通过其履带进行有效移动。你可以用系统控制这n台机器。你控制的机器编号为1~n。机器人之间也能互相传输数据,但是要满足一个非常苛刻的条件:每两台机器人之间距离为d,d一定为正数,没有两台机器人位置相同。你想要最小化最大距离,注意两台以上机器人在同一洞穴是可以的。
给定n台机器人现在的位置,写一个程序,让机器人们走过的最大距离最小,并输出最小化的最大距离。
输入
n(机器人台数) d(两台机器人间的距离)
第二行是n个(-1e16~1e16)的数字,代表着机器人们的位置。
输出
最小化的最大距离
why TLE 36????换了vector+max_element没超时,难道是max函数的原因??
注意单调递增和单调递减两种情况。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e6+10; ll n,d; ll a[maxn]; ll solve(ll d){ ll noww=a[1]; vector<ll> vec; vec.push_back(0); for(int i=2;i<=n;i++){ noww+=d; vec.push_back(a[i]-noww); } ll maxx=*max_element(vec.begin(),vec.end()); ll minn=*min_element(vec.begin(),vec.end()); return maxx-minn; } int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); cin>>n>>d; for(int i=1;i<=n;i++) cin>>a[i]; ll ans=solve(d); ans=min(ans,solve(-d)); cout<<ans/2<<"."<<(ans%2?5:0)<<"\n"; return 0; }
H
三条间距相等的平行直线上各有一些点,在每条直线各选一点,求三点共线的直线条数。
实际上就是x1+x3=2x2,轻松过样例。但是时间复杂度要降到O(N^2)以下,显然不会,应该是知识点的缺失。
I
没看
J
A喜欢旅游,她到了一个非常有趣的宾馆。N个灯N个开关。有趣的是,A打开一个开关,发现不止一盏灯亮着的。并且,再打开一些开关会发现原来一些开过来的灯暗了下去。幸运的是,当所有开关关闭的
时候,灯也都是关闭的。
为了弄明白每盏灯怎样被开关影响,A做以下步骤进行实验。首先,每盏灯和每个开关被标上序号,这样它们可以被分清。一开始,开关全部关闭。然后,她把每盏灯开完又关闭来检查。
知识点缺失,不会
K
图把我劝退
L

晚上补一下知识点:
Gauss消元
板子题:https://www.luogu.com.cn/problem/P3389
//Gauss消元模板 //1.选择一个尚未被选过的未知数作为主元,选择一个包含这个主元的方程。 //2.将这个方程主元的系数化为1。 //3.通过加减消元,消掉其它方程中的这个未知数。 //4.重复以上步骤,直到把每一行都变成只有一项有系数。 #include<bits/stdc++.h> using namespace std; #define re register typedef long long ll; typedef double db; const int maxn=1e2+30; db mat[maxn][maxn]; int n; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); cin>>n; for(re int i=1;i<=n;i++){ for(re int j=1;j<=n+1;j++) { cin>>mat[i][j]; } } for(re int i=1;i<=n;i++){//枚举列 re int mx=i; for(re int j=i+1;j<=n;j++)//选出该列最大系数 { if(fabs(mat[j][i])>fabs(mat[mx][i])) mx=j; } for(re int j=1;j<=n+1;j++)//exchange { swap(mat[i][j],mat[mx][j]); } if(!mat[i][i])//最大值等于0则说明该列都为0,肯定无解 { cout<<"No Solution"<<"\n"; return 0; } for(re int j=1;j<=n;j++){//每一项都减去一个数(即加减消元) if(j!=i){ re db tmp=mat[j][i]/mat[i][i]; for(re int k=i+1;k<=n+1;k++) { mat[j][k]-=mat[i][k]*tmp;//减去一个数(消元) } } } } for(re int i=1;i<=n;i++) cout<<fixed<<setprecision(2)<<mat[i][n+1]/mat[i][i]<<"\n"; return 0; }
FFT:(没学会)
优质FFT博客(非本人)
链接:https://www.cnblogs.com/zwfymqz/p/8244902.html#_label0

浙公网安备 33010602011771号