Codeforces Round 988 (Div. 3) 个人题解 (A~G)
Codeforces Round 988 (Div. 3) 个人题解 (A~G)
Codeforces Round 988 (Div. 3)
解题思路
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
int a[30];
void solve(){
int n;cin>>n;
map<int,int> mp;
for(int i=1;i<=n;i++){
cin>>a[i];
mp[a[i]]++;
}
int ans=0;
for(auto [x,v]:mp){
ans+=v/2;
}
cout<<ans<<endl;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
解题思路
- 去掉行,列的输入则还剩下k-2个值,直接暴力枚举(k-2)的因数,判断有没有出现过即可
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N=2e5+10;
int a[N];
void solve(){
int k;cin>>k;
map<int,int> mp;
for(int i=1;i<=k;i++){
cin>>a[i];
mp[a[i]]=1;
}
int t=k-2;
for(int i=1;i<=t;i++){
if(t%i) continue;
int n=i,m=t/i;
if(mp[n] && mp[m]){
cout<<n<<" "<<m<<endl;
return;
}
}
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
解题思路
- 构造题,首先想到,n=1时答案为1,1<=n<=4时无解,n>=5时考虑构造
- 构造序列3,1,5,4,2,这是n=5时的答案,接下来只需奇数往前插入,偶数往后插入,(这样的话新产生的和一定为偶数且大于2),这样构造即可
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
void solve(){
int n;cin>>n;
if(n==1){
cout<<1<<endl;
return;
}
if(n<=4){
cout<<-1<<endl;
return;
}
deque<int> q;
q.push_back(3);
q.push_back(1);
q.push_back(5);
q.push_back(4);
q.push_back(2);
for(int i=6;i<=n;i++){
if(i&1) q.push_front(i);
else q.push_back(i);
}
for(auto x:q) cout<<x<<" ";
cout<<endl;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
解题思路
- 感觉是模拟题?
- 遇到一个障碍跳不过去的时候,考虑要去踩装置获得能量,那只需要将能量加到能够越过障碍即可,所以考虑踩当前能踩到的加最大能量的装置,用一个大根堆维护一下,枚举障碍即可
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int ll[N],r[N],xx[N],v[N];
void solve(){
int n,m,l;cin>>n>>m>>l;
for(int i=1;i<=n;i++) cin>>ll[i]>>r[i];
for(int i=1;i<=m;i++) cin>>xx[i]>>v[i];
int idx=1,ans=0;
int k=1;
priority_queue<int> q;
for(int i=1;i<=n;i++){
int len=r[i]-ll[i]+2;
while(idx<=m && xx[idx]<ll[i]){
q.push(v[idx]);
idx++;
}
while(q.size() && k<len){
int t=q.top();
q.pop();
ans++;
k+=t;
}
if(k<len){
cout<<-1<<endl;
return;
}
}
cout<<ans<<endl;
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
解题思路
- 交互题,太久没做了快忘记怎么做了
- 考虑每次询问(1,k),设每次询问的结果为a[i],若a[i]=a[i-1],则ans[i]=0,若a[i]>a[i-1],则ans[i]=1,(这是在前面已经出现0的情况下)
- 那如何判断前导的1呢?若为11000111,那么在前面的询问中a[i]=a[i-1],我们只需要找到第一个a[i]!=0位置id.a[id]表示前id-1个数里有a[id]个0,那么最左端则有id-1-a[id]个1
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int a[N],ans[N];
int ask(int x){
cout<<"? "<<1<<" "<<x<<endl;
int res;cin>>res;
return res;
}
void solve(){
int n;cin>>n;
for(int i=1;i<=n;i++) ans[i]=0;
for(int i=2;i<=n;i++) a[i]=ask(i);
if(a[n]==0){
cout<<"! IMPOSSIBLE"<<endl;
return;
}
for(int i=2;i<=n;i++){
if(a[i]>a[i-1]) ans[i]=1;
}
for(int i=2;i<=n;i++){
if(ans[i]==1){
int tmp=a[i];
for(int j=1;j<=i-1-a[i];j++) ans[j]=1;
}
}
cout<<"! ";
for(int i=1;i<=n;i++) cout<<ans[i];
cout<<endl;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
题目分析
- 二分答案,我们考虑对于一个地方进攻cnt次,那么每个地方每次收到的伤害至少为\(\lceil h[i]/cnt \rceil\),假设该值为t,那么进攻范围则应该在[x[i]-m+t,x[i]+m-t],我们队每个敌人都处理出这样一个区间,接下来比较经典,查询是否有被覆盖超过k次的点,可以差分加离散化处理,也可以直接差分累加判断
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
const int N=1e5+10;
using namespace std;
int n,m,k;
int h[N],x[N];
typedef pair<int,int> PII;
bool check(int cnt){
vector<PII> v;
for(int i=1;i<=n;i++){
if(m*cnt<h[i]) continue;
int t=(h[i]+cnt-1)/cnt;
int l=x[i]-m+t;
int r=x[i]+m-t;
v.push_back({l,1});
v.push_back({r+1,-1});
}
sort(v.begin(),v.end());
int res=0;
for(auto [s,y]:v){
res+=y;
if(res>=k) return true;
}
return false;
}
void solve(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>h[i];
for(int i=1;i<=n;i++) cin>>x[i];
int ans=-1;
int l=1,r=1e9;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
r=mid-1;
ans=mid;
}
else l=mid+1;
}
cout<<ans<<endl;
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
题目分析
- 考虑O(n2)朴素dp,f[i]表示到达i点时的方案总数,则f[i]=\(\sum_{j=1}^{i-1} f_j\) (gcd(a[i],a[j])!=1),这样就是n方的dp,考虑优化
- gcd(a[i],a[j]!=1)实际上就是两数至少有一个相同的质因子,我们可以这样考虑,假设a[i]分解出的质数有2,3,5,那么f[i]需要加上的则为2,3,5的所有组合,这里可以状压处理,但是这中间算重合的部分如何处理?考虑容斥计算
- 我们记录sum[i]为前面中所有含有i因子的状态总和,则f[i]=sum[2]+sum[3]+sum[5]-sum[6]-sum[10]-sum[15]+sum[30],即容斥的去算
- 处理每个数的质因子的部分可以先预处理出每个数的最小质因子,这样能够加速预处理
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e6+10;
const int MAXN=1e6;
const int mod=998244353;
int tot=0;
int a[N],pri[N],vis[N],mn[N],f[N],sum[N];
vector<int> g[N];
void init(){
for(int i=2;i<=MAXN;i++){
if(!vis[i]){
pri[++tot]=i;
mn[i]=i;
}
for(int j=1;j<=tot && pri[j]*i<=MAXN;j++){
int num=pri[j]*i;
vis[num]=1;
mn[num]=pri[j];
if(i%pri[j]==0) break;
}
}
}
void solve(){
init();
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
int tmp=a[i];
while(tmp!=1){
int v=mn[tmp];
g[i].push_back(v);
while(tmp%v==0) tmp/=v;
}
}
f[1]=1;
for(int i=1;i<=n;i++){
vector<int> v;
int cnt=g[i].size();
for(int j=1;j<(1ll<<cnt);j++){
int mul=1,p=-1;
for(int k=0;k<cnt;k++){
if((j>>k)&1){
mul*=g[i][k];
p*=-1;
}
}
v.push_back(mul);
f[i]+=p*sum[mul]%mod;
f[i]%=mod;
}
for(auto x:v){
sum[x]+=f[i];
sum[x]%=mod;
}
}
int ans=(f[n]+mod)%mod;
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T=1;
while(T--) solve();
return 0;
}