cf 板刷日记(1700-1900)一 11题
D. Insolvable Disks
Codeforces Global Round 31 (Div. 1 + Div. 2)
https://codeforces.com/problemset/problem/2180/D
想了个非常复杂的 DP,写了一个多小时发现是错的
大致思路就是,从第一个点开始,从前往后贪心的去和下一个圆相切,直到某一个位置不能满足相切的条件,就断开,我们的目的是让断开次数尽可能小。
先证明贪心的正确性,设某个序列的前五个点是 A,B,C,D,E。
从 A 开始,假设 A 可以连到 C,但连不到 D;从 D 开始,D 可以连到 E。则此时的切割方案是:{ABC},{DE}
但如果令 A 只连到 B 就切割,而从 C 开始也能练到 E。则此时的切割方案是:{AB},{CDE}
对比两种方案,虽然都是分成了两段,但因为 {DE} 和 {CDE} 的起点不同,导致假设这两个序列如果都能往后面继续连,则 {DE} 往后连的距离一定比 {CDE} 要不短。
因为 {CDE} 对比 {DE},显然多了一个点,所以多了一个限制条件。
所以贪心是对的。
接下来考虑如何做,先特判 \(n==1\) 的情况。
初始时,在第一个点,这个点 \(r\) 的范围是:\([0, a[2]-a[1]]\)
然后从 \(i=2\) 开始循环,利用上一个点 \(r\) 的范围,计算出当前点 \(r\) 的范围,如果当前点 \(r\) 的范围不合法,则需要切割一次,然后以当前点为起点,开始一个新的链。
每次切割时,\(r_i\) 的范围 \([x_i,y]\) 中,\(x_i\) 显然等于 \(0\),而 \(y_i\) 的值应该是 \(a[i]-a[i-1]-y_{i-1}\);因为不能和上一个点的面积重合
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
using pdd=pair<double,double>;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n;
cin>>n;
vector<int> a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
if(n==1){
cout<<0<<endl;
return;
}
int ans=1;
int l=0,r=a[2]-a[1];
for(int i=2;i<n;i++){
int nl=max(0ll,a[i]-a[i-1]-r);
int nr=min(a[i]-a[i-1]-l,a[i+1]-a[i]);
if(nl<nr){
ans++;
l=nl,r=nr;
}
else{
r=a[i]-a[i-1]-l,l=0;
}
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
C. XOR-factorization
Codeforces Global Round 31 (Div. 1 + Div. 2)
https://codeforces.com/problemset/problem/2180/C
当 \(n\) 为奇数时,显然全 \(n\) 即可
当 \(n\) 为偶数时,有个很显然的猜测,前 \(n-1\) 个为 \(n\),最后一个为 \(0\)
但是更优解法,按照二进制位从高到低考虑
情况 \(1\):如果 \(n\) 的这一位是 1,则需要随便让一个数的这一位为 0,其他 \(n-1\) 个数的这一位为 1。
情况 \(2\):如果 \(n\) 的这一位是 0,为了保证每一个数都不大于 \(n\),所以需要让所有数的这一位为 0。但是有一个例外:如果某个数,在计算更高位时,在情况一中被选中,则在此时,这一位可以是 1。
影响最优总和大小的。显然只有情况 \(2\) 中,原本是 0 但改为 1 的情况,为了让满足 0 改为 1 条件的数尽可能多,所以在情况 \(1\) 中,要尽可能的选择不同的数。
注意,在计算情况 \(2\) 时,要两个数两个数的操作
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n,k;
cin>>n>>k;
vector<int> a(k+1),st(k+1);
if(k%2==1){
for(int i=1;i<=k;i++){
cout<<n<<" ";
}
cout<<endl;
return;
}
for(int i=31;i>=0;i--){
if((n>>i)&1){
int pos=k;
for(int j=1;j<=k;j++){
if(!st[j]){
pos=j;
}
}
st[pos]=1;
for(int j=1;j<=k;j++){
if(j==pos) continue;
a[j]|=1<<i;
}
}
else{
int lst=0;
for(int j=1;j<=k;j++){
if(!st[j]) continue;
if(lst==0) lst=j;
else{
a[lst]|=1<<i;
a[j]|=1<<i;
lst=0;
}
}
}
}
for(int i=1;i<=k;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
D. Fibonacci Paths
Codeforces Round 1070 (Div. 2)
https://codeforces.com/problemset/problem/2176/D
图上 DP,设 \(dp[i]\) 表示,以第 \(i\) 条边结尾,的斐波那契边数量
设 \(mp[i] [j]\) 表示,以点 \(i\) 结尾,且最后一条边的边权为 \(j\) 的边数量
考虑 DP 的顺序,设某条边 \((v->u)\) 的边权值为 \(v+u\),则 DP 一定是按照边权从小到大转移,所以对所有边,按照边权从小到大排序
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n,m;
cin>>n>>m;
vector<int> a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
vector<pii> g(m+1);
for(int i=1;i<=m;i++){
cin>>g[i].first>>g[i].second;
}
sort(g.begin()+1,g.end(),[&](pii t1,pii t2)-> bool {
return a[t1.first]+a[t1.second]<a[t2.first]+a[t2.second];
});
vector<map<int,int>> mp(n+1);
vector<int> f(m+1);
int ans=0;
for(int i=1;i<=m;i++){
auto [v,u]=g[i];
int sum=a[v]+a[u];
f[i]=1;
f[i]=(f[i]+mp[v][a[u]])%mod;
mp[u][sum]=(mp[u][sum]+f[i])%mod;
ans=(ans+f[i])%mod;
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
B. Wishing Cards
Codeforces Round 1069 (Div. 1)
https://codeforces.com/problemset/problem/2174/B
观察 \(1\):序列 \(b\) 的非 \(0\) 位置单调递增。
观察 \(2\):若 \(x\) 出现在 \(b_i\) 中,则其一定出现在第一个满足 \(a_i ≥ x\) 的位置 \(i\) 上。
设 \(f[i] [j] [x]\) : 选第 \(i\) 个,且当前一共选了 \(j\) 张牌,且上一个选的是 \(x\) 的最大值
做一个 \(k^3\) 的 DP 即可
D. Taiga's Carry Chains
Codeforces Round 1068 (Div. 2)
https://codeforces.com/problemset/problem/2173/D
首先需要发现,对 \(n\) 进行 \(k\) 次操作后,得到结果 \(res\),此时过程中产生的进位次数等于 \(popcount(n) + k - popcount(res)\)
所以目的就是最小化 \(popcount(res)\)
等价于,对于 \(n\) 的二进制位,需要做 \(k\) 次操作,使得最后结果的二进制位为 \(1\) 的位数最小
对 \(n\) 的二进制位,设 \(f[i] [j] [x]\) 为到第 \(i\) 位,操作 \(j\) 次,给第 \(i+1\) 位的进位是 \(x\) 的最小 \(popcount\) 值
顺序 DP 可能不好写,记忆化实现即可
B. Marble Council
Codeforces Round 1064 (Div. 1)
https://codeforces.com/problemset/problem/2165/B
非常妙且有难度的一道题
设最后的多重集为 \(S\)
所有不在集合中的数 \(y\) 中,\(cnty\) 的最大值为 \(maxcnt\)
所有在集合中的数 \(x\),\(sum(cntx)=sum\)
若 \(S\) 合法,则 \(sum>=maxcnt\) ( \(x\) 不能成为唯一众数)
设 \(mx\) 为初始所有值 \(a[i]\) 的最大 \(cnt(a[i])\)
若 \(sum>=mx\),则此时集合 \(S\) 一定合法
若 \(sum<mx\),则此时集合 \(S\) 一定不合法
设 \(dp_i\) 表示 \(sum=i\) 的集合数量,则此时 \(ans=sum(dp_i) (i>=mx)\)
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n;
cin>>n;
vector<int> a(n+1),cnt(n+1);
int mx=0;
for(int i=1;i<=n;i++){
cin>>a[i];
cnt[a[i]]++;
mx=max(mx,cnt[a[i]]);
}
vector<int> f(n+1,0);
f[0]=1;
for(int val=1;val<=n;val++){
if(cnt[val]==0) continue;
for(int j=n-cnt[val];j>=0;j--){
f[j+cnt[val]]+=f[j]*cnt[val];
f[j+cnt[val]]%=mod;
}
}
int ans=0;
for(int i=mx;i<=n;i++){
ans=(ans+f[i])%mod;
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
D. Copy String
Codeforces Global Round 30 (Div. 1 + Div. 2)
https://codeforces.com/problemset/problem/2164/D
模拟一下结束了,这为什么能 1800
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n,k;
cin>>n>>k;
vector<vector<int>> a(k+2,vector<int>(n+1));
vector<int> pos(n+1);
string s,t;
cin>>s>>t;
s=" "+s;
t=" "+t;
int idx=n;
for(int i=n;i>=1;i--){
if(t[i]==s[idx] && idx<=i) continue;
// if(i!=n && t[i]==t[i+1] && idx<=i) continue;
while(idx>1 && (t[i]!=s[idx] || idx>i)){
idx--;
}
if(t[i]!=s[idx]){
cout<<-1<<endl;
return;
}
int now=1;
for(int j=idx+1;j<=i;j++){
now=max(now,pos[j]+1);
pos[j]=now;
if(now>k){
cout<<-1<<endl;
return;
}
a[now][j]=1;
now++;
}
}
int mx=0;
for(int i=1;i<=n;i++){
mx=max(mx,pos[i]);
}
cout<<mx<<endl;
for(int i=1;i<=mx;i++){
t=s;
for(int j=1;j<=n;j++){
if(a[i][j]==1) t[j]=s[j-1];
cout<<t[j];
}
s=t;
cout<<endl;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
B. Siga ta Kymata
Codeforces Round 1063 (Div. 2)
https://codeforces.com/problemset/problem/2163/B
找最大值和最小值就行,特判一下最大最小值的位置和两端
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n;
cin>>n;
vector<int> a(n+1);
int mn,mx;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]==1) mn=i;
if(a[i]==n) mx=i;
}
string s;
cin>>s;
s=" "+s;
if(s[1]=='1' || s[n]=='1' || s[mn]=='1' || s[mx]=='1'){
cout<<-1<<endl;
return;
}
cout<<5<<endl;
cout<<min(mn,mx)<<" "<<max(mn,mx)<<endl;
cout<<1<<" "<<mn<<endl;
cout<<1<<" "<<mx<<endl;
cout<<mn<<" "<<n<<endl;
cout<<mx<<" "<<n<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
B. Make Connected
Pinely Round 5 (Div. 1 + Div. 2)
https://codeforces.com/problemset/problem/2161/B
等价于判断所有点是不是在两条相邻的斜线上,可以用截距来判断。
需要特判一个 2*2 的正方形
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n;
cin>>n;
vector<pii> p;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
char ch;
cin>>ch;
if(ch=='#'){
p.push_back({i,j});
}
}
}
if(p.size()==0){
cout<<"YES\n";
return;
}
if(p.size()==4){
set<int> x,y;
for(auto [a,b]:p){
x.insert(a);
y.insert(b);
}
if(x.size()==2 && y.size()==2){
if(*max_element(x.begin(),x.end())==*min_element(x.begin(),x.end())+1){
if(*max_element(y.begin(),y.end())==*min_element(y.begin(),y.end())+1){
cout<<"YES\n";
return;
}
}
}
}
if(p.size()==0){
cout<<"YES\n";
return;
}
int b=p[0].second-p[0].first,t=inf;
int f=2;
for(auto [x,y]:p){
if(y-x==b) continue;
else{
if(t==inf){
t=y-x;
if(abs(b-t)>=2){
f--;
break;
}
}
else{
if(y-x!=t){
f--;
break;
}
}
}
}
b=p[0].second+p[0].first,t=inf;
for(auto [x,y]:p){
if(y+x==b) continue;
else{
if(t==inf){
t=y+x;
if(abs(b-t)>=2){
f--;
break;
}
}
else{
if(y+x!=t){
f--;
break;
}
}
}
}
if(f==0) cout<<"NO\n";
else cout<<"YES\n";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
A. MAD Interactive Problem
Codeforces Round 1058 (Div. 1)
https://codeforces.com/problemset/problem/2159/A
用一个 \(vector\) 维护当前查询的下标,这个 \(vector\) 只记录第一次出现的数的位置。
从前往后,把当前数加进 \(vector\) 去查询
如果此时查询结果不等于 \(0\),则这个位置的数就是本次查询结果,并再把这个位置从 \(vector\) 中删掉
如果此时查询的结果等于 \(0\),说明这个位置的数第一次在排列中出现, \(vector\) 中保留这个位置
处理完后,再用所有确定位置的数去查不确定的数即可。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
int query(vector<int> t){
cout<<"? "<<t.size()<<" ";
for(auto val:t){
cout<<val<<" ";
}
cout<<endl;
int res;
cin>>res;
return res;
}
void solve(){
int n;
cin>>n;
vector<int> t,a(2*n+1);
for(int i=1;i<=2*n;i++){
t.push_back(i);
int res=query(t);
if(res==0) continue;
a[i]=res;
t.pop_back();
}
t.clear();
for(int i=1;i<=2*n;i++){
if(a[i]) t.push_back(i);
}
for(int i=1;i<=2*n;i++){
if(a[i]) continue;
auto tmp=t;
tmp.push_back(i);
sort(tmp.begin(),tmp.end());
int res=query(tmp);
// cout<<i<<" -- "<<res<<endl;
a[i]=res;
}
cout<<"! ";
for(int i=1;i<=2*n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
E. Adjusting Drones
Codeforces Round 1066 (Div. 1 + Div. 2)
https://codeforces.com/problemset/problem/2157/E
统计所有数的出现次数,从小到大遍历每个数,设计数器初始值为 0,如果本次需要将 \(cnt(n)-1\) 个数变成 \(n+1\),则计数器++,否则置零
记录过程中计数器的最大值即可
很迷,感觉这么做就是对的。一开始写了个很复杂的数据结构维护的做法 wa 2了。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n,k;
cin>>n>>k;
vector<int> a(n+1);
vector<int> cnt(4*n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
cnt[a[i]]++;
}
int ans=0,now=0;
for(int i=1;i<=4*n;i++){
if(cnt[i]<=k){
ans=max(ans,now);
now=0;
continue;
}
cnt[i+1]+=cnt[i]-1;
cnt[i]=1;
now++;
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}

浙公网安备 33010602011771号