Codeforces Round #615 (Div. 3)
B
有n个盒子需要你捡,第i个盒子的坐标为 (Xi , Yi)。你从(0,0)出发,每次只能选择向上或者向右移动,问能否将n个盒子都捡完,若可以捡完,则输出字典序最小的一条路线
简单模拟
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2005;
int b,c,n,m,T;
int L[1001],R[1001];
struct node{
int x,y;
}a[N];
string s;
bool cmp(node p,node pp)
{
if(p.x==pp.x) return p.y<pp.y;
else return p.x<pp.x;
}
int main()
{
scanf("%d",&T);
while(T--){
int vis=0;
s.clear();
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
}
// puts("bug");
sort(a+1,a+1+n,cmp);
int lastx=0,lasty=0;
for(int i=2;i<=n;i++){
if(a[i].x!=a[i-1].x&&a[i].y<a[i-1].y) {vis=1;break;}
}
if(vis) {puts("NO");continue;}
puts("YES");
for(int i=1;i<=n;i++) {
for(int j=lastx;j<a[i].x;j++) s.push_back('R');
for(int j=lasty;j<a[i].y;j++) s.push_back('U');
lastx=a[i].x,lasty=a[i].y;
}
cout<<s<<endl;
}
}
C
给你一个 n ,要求三个整数 a ,b ,c 使得 a * b * c = n 并且 a、b、c >= 2
分析:
先枚举 n 的因子,再判断因子是否可以再分解即可
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
vector<int> a;
typedef long long ll;
int main()
{
int n,t;
scanf("%d",&t);
while(t--){
int cnt=0;
a.clear();
scanf("%d",&n);
for(ll i=2;i*i<=n;i++){
if(n%i==0){
a.push_back(i);
cnt++;
n/=i;
}
if(cnt==3) break;
}
if(n!=1){
if(a.size()==3) a.back()*=n;
else a.push_back(n);
}
if(a.size()<3) cout<<"NO"<<endl;
else{
if(a[0]==a[1]||a[1]==a[2]||a[0]==a[2]) cout<<"NO"<<endl;
else cout<<"YES"<<endl,cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl;
}
}
return 0;
}
D
给你 q 个询问和 一个 x , 每次询问输入一个数 n ,你可以把它与当前数组进行减任意次 x 或 加任意次 x,然后添入数组,问每次询问结束时数组里最小的没出现的非负整数是多少;
解法:
观察数据再手搓画一下我们发现,m>=x是“没有作用”的。解释就是每个元素你不断加多次x都无所谓,因为他的题意是其中的最小值,
我们可以将已给区间看成无数个【0,x-1】;
|——————|—|——————|—|——————————————|
0 x-1 0 x-1 0 ........
因为每个元素加x或减掉x,实际上就在这些间隔为x的新元素中寻找最小,例如m=5,x=3,可以推出2,5,8,11....,此时a=2;
此时最小是0,其中再来个m=0,此时a=0,2;最小是1,再来个m=1,a=0,1,2,最小是3,再来个m=10,可有1,4,7,10,13....,你会发现我们只需要他造出来的第一个区间里没有的例如10传出来的1,4区间有了,但是7没有,那就把7加进去,所以我们发现一个元素只给区间贡献一个规律的位置,例如10闯出来的,其实mod x后就是1,所以我们将坐标看成上面的,每次元素我们都mod x,进行记录次数,表示区间有多少个该位置被占了,然后在遍历一遍来mex
cnt [i] 表示此次询问时,若干个区间一共有cnt[i] 个 i 可以填到若干个区间中的 i 位置上(为了满足题目要求,我们从第一个区间的第i个位置开始填,然后再填第二个区间第i个位置)
然后我们从第一个区间开始检查。若到当前位置时cnt[i] != 0,则我们让cnt[i] --(表示我们拿一个i填在这个位置上),同时往下一个位置跳,直到遇到一个没有数可填的位置——cnt[pos] = 0
#include<bits/stdc++.h>
using namespace std;
map<int , int>cnt;
int main()
{
int q , x , ans = 0;
cin >> q >> x;
while(q --)
{
int n;
cin >> n;
cnt[n % x] ++;
while(cnt[ans % x])
cnt[ans % x] -- , ans ++;
cout << ans << '\n';
}
return 0;
}
E
题意:
给你一个 n * m 的矩阵,你执行两种操作:
① 把矩阵中任意一个元素改为任意一个数
② 把矩阵中任意一列整体往上挪一个单元格,如下图矩阵我们对第一列向上挪了一个单元格

现要求用最少的操作次数使矩阵内每一个元素 a[i][j] = (i - 1) * m + j
分析:
因为题目只能对一列或者一个元素进行操作,所以我们逐列进行维护。
对第i行第j列的元素a[i][j] 我们假设它将成为这列的起点(第一个元素) 那么最坏的操作次数cost[i]为 i + N (把它移动到第1位需要i次 如果元素全都很奇葩需要更改N次)
对于每一列的操作,我们先初始化cost[i] = i + N , 然后如果a[i][j]可以作为第h行的答案的答案,那么cost[h] --(把a[h][j]行设为起点的最坏操作- 1)
最后遍历cost[1] ~ cost[n] 挑选最小的cost加到ans里即可
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int main()
{
int n , m ;
cin >> n >> m;
vector<vector<int>>a(n , vector<int>(m));
for(int i = 0 ; i < n ; i ++) for(int j = 0 ; j < m ; j ++)
{
cin >> a[i][j];
a[i][j] --;
}
int ans = 0;
for(int j = 0 ; j < m ; j ++)
{
vector<int>cost(n);
for(int i = 0 ; i < n ; i ++) cost[i] = i + n;
for(int i = 0 ; i < n ; i ++)
{
if(a[i][j] % m == j && a[i][j] < n * m)
{
int h = i - a[i][j] / m;
if(h < 0) h += n;
cost[h] --;
}
}
ans += *min_element(cost.begin() , cost.end());
}
cout << ans << '\n';
return 0;
}
F
给你一棵树,要求你找出任意三点 A,B,C,使得 A~B,B~C,A~C 之间的边最多(边并集最大)
分析:
可证明一组最优解中一定有两个点是直径的两端点,那么题目就转换成求树直径端点及与两端点边并集最大的点,于是就很简单了
我们先一次bfs求出树直径DIS即其一端点A,再对端点A进行bfs求出另一端点B及每个点到端点A的距离dis1[i],最后再bfs端点B求出每个点到B的距离dis2[i]
最后遍历每个点,取边并集最大的即可(边并集= dis1[i]+dis2[i]−DIS2+DISdis1[i]+dis2[i]−DIS2+DIS)
现在给出证明:
假设某个答案取连接点x。x最远的树到达的点是s,根据树的直径算法,s是树的某个直径a的端点。假设x的最远和第二远的点组成的链是b,b就会和a有一段公共部分。我们取a和b相交部分距离s最远的那个点y。那么取这个链上点y的答案一定比x更优
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
int n,m,T,tot,d[maxn],h[maxn*2],vis[maxn],dis1[maxn],dis2[maxn];
int DIS;
struct node
{
int w,nxt,to;
}edg[maxn*2];
void add(int u,int v,int w)
{
edg[tot].nxt=h[u];
edg[tot].w=w;
edg[tot].to=v;
h[u]=tot++;
}
int dfs(int x)
{
int u;
memset(d,0,sizeof(d));
memset(vis,0,sizeof(vis));
queue<int>q;
q.push(x);
vis[x]=1;
while(!q.empty()){
u=q.front();
q.pop();
for(int i=h[u];~i;i=edg[i].nxt){
int to=edg[i].to;
if(vis[to]) continue;
d[to]=d[u]+edg[i].w;
vis[to]=1;
q.push(to);
DIS=max(DIS,d[to]);
}
}
return u;
}
int main()
{
memset(h,-1,sizeof(h));
int a,b;
cin>>n;
int ans=0;
for(int i=1;i<n;i++){
cin>>a>>b;
add(a,b,1);
add(b,a,1);
}
int one,two,three;
one=dfs(1);
two=dfs(one);
for(int i=1;i<=n;i++) dis1[i]=d[i];
dfs(two);
for(int i=1;i<=n;i++) dis2[i]=d[i];
for(int i=1;i<=n;i++){
int res=(dis1[i]+dis2[i]-DIS)/2+DIS;
if(ans<res&&i!=one&&i!=two){
three=i;
ans=res;
}
}
cout<<ans<<'\n'<<one<<" "<<two<<" "<<three<<endl;
}
浙公网安备 33010602011771号