第29次CSP认证 2022/3/29 个人题解
425分,前三题100,第四题90,第五题35。第四题离散化线段树,测试点10和11过不去,可能出了小bug,要是大伙有看出来什么问题的可以和我说下。
田地丈量
就所有坐标枚举硬怼,感觉复杂度有超的可能,但是过了就没管
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
int multi=false;
const int N=1e4+13;
int mp[N][N];
void solve(){
int n,a,b;cin>>n>>a>>b;
for(int i=1;i<=n;i++){
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
for(int i1=max(0,x1);i1<=x2-1;i1++)
for(int j1=max(0,y1);j1<=y2-1;j1++)mp[i1][j1]=1;
}
int res=0;
for(int i=0;i<a;i++)for(int j=0;j<b;j++)if(mp[i][j])res++;
cout<<res<<endl;
}
signed main(){
int t=1;
if(multi)cin>>t;
while(t--)solve();
}
/*
4 10 10
0 0 5 5
5 -2 15 3
8 8 15 15
-2 10 3 15
*/
垦田计划
小小的二分一下
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
int multi=false;
const int N=1e5+13;
int t[N],c[N];
int n,m,k;
bool check(int x){
int res=0;
for(int i=1;i<=n;i++){
if(t[i]>=x)res+=c[i]*(t[i]-x);
}
//cout<<x<<" "<<res<<endl;
if(res>m)return false;
return true;
}
void solve(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++)cin>>t[i]>>c[i];
int l=k,r=*max_element(t+1,t+1+n);
while(l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
cout<<l<<endl;
}
signed main(){
int t=1;
if(multi)cin>>t;
while(t--)solve();
}
/*
4 9 2
6 1
5 1
6 2
7 1
*/
LDAP
模拟的很痛苦,大体思路就是先把字符串递归出所有条件,然后再进行枚举。写的不好,不开unordered_map只能过70分,开了能水过,还好数据没搞那种卡n2的玩意,不然这场炸了。
#include<iostream>
#include<map>
#include<vector>
#include<algorithm>
#include<unordered_map>
using namespace std;
#define endl "\n"
int multi=false;
int n,dn[2502];
unordered_map<int,int> mp[2502];
bool ok(string s,int x){
if(s[0]=='|'){
string s1,s2;
int idx=0,stk=0;
for(int i=1;i<s.size();i++){
if(s[i]==')'){
if(--stk==0){
idx=i;break;
}
}else if(s[i]=='(')stk++;
}
s1=s.substr(2,idx-2);
s2=s.substr(idx+2);//cout<<s2<<endl;
s2=s2.substr(0,s2.size()-1);//cout<<s2<<endl;
if(ok(s1,x)||ok(s2,x))return true;
return false;
}else if(s[0]=='&'){
string s1,s2;
int idx=0,stk=0;
for(int i=0;i<s.size();i++){
if(s[i]==')'){
if(--stk==0){
idx=i;break;
}
}else if(s[i]=='(')stk++;
}
s1=s.substr(2,idx-2);
s2=s.substr(idx+2);
s2=s2.substr(0,s2.size()-1);
if(ok(s1,x)&&ok(s2,x))return true;return false;
}else{
int split=s.find(":");
if(split!=s.npos){
//cout<<split<<" "<<s.substr(0,split)<<" "<<s.substr(split+1);
int val1=stoi(s.substr(0,split)),val2=stoi(s.substr(split+1));
if(mp[x][val1]==val2)return true;return false;
}else{
split=s.find("~");
int val1=stoi(s.substr(0,split)),val2=stoi(s.substr(split+1));
if(mp[x][val1]!=val2&&mp[x][val1]!=0)return true;return false;
}
}
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>dn[i];
int cnt;cin>>cnt;
for(int j=1;j<=cnt;j++){
int a,b;cin>>a>>b;
mp[i][a]=b;
}
}
int q;cin>>q;
while(q--){
string s;cin>>s;
vector<int> ans;
for(int i=1;i<=n;i++)if(ok(s,i))ans.push_back(dn[i]);
//for(auto& v:res)cout<<v<<" "<<dn[v]<<endl;
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++)cout<<ans[i]<<" \n"[i==int(ans.size())-1];
if(ans.size()==0)cout<<endl;
}
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t=1;
if(multi)cin>>t;
while(t--)solve();
}
/*
2
1 2 1 2 2 3
2 2 2 3 3 1
40
1:2
3~1
&(1:2)(2:3)
|(1:2)(3:1)
|(3~1)(3:1)
&(&(1:2)(2:3))(3:1)
*/
星际网络II 90分
看这样子就像线段树,数据范围这么大,所以肯定需要离散化。输入格式很怪,很明显不能用long long存。好在都是定长字符串,字符串直接比大小没有问题。然后需要先把所有query出现的数据都存进去,所以要先读一遍query,把query的内容存到一个vector里,然后再读vector,写的也很难受。我用的是map存储,也可以lower_bound,可能会快一点?然后把离散化以后的点进行线段树的基本操作就行了,线段树写起来还是很快的。当然这里有个小细节,如果两个相邻的数字差值大于1,那么就需要在这两个数之间插入一个点来防止出现问题。细节看代码
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
int multi=false;
const int N=2e5+3;
set<string> s;
map<string,int> mp;
int nowsz;
struct tree{
int col,difcol,lz,sz;
}t[N<<2];
void pushup(int id){
if(t[id<<1].col==t[id<<1|1].col||(t[id<<1].col==0||t[id<<1|1].col==0)){
t[id].difcol=t[id<<1].difcol|t[id<<1|1].difcol;
}else t[id].difcol=1;
if(t[id].difcol==1)t[id].col=0x3f3f3f3f;
else t[id].col=t[id<<1].col|t[id<<1|1].col;//may has problem?
t[id].sz=t[id<<1].sz+t[id<<1|1].sz;
}
void pushdown(int l,int r,int id){
int mid=l+r>>1;
if(t[id].lz){
int col=t[id].lz;t[id].lz=0;
t[id<<1].col=t[id<<1|1].col=col;
t[id<<1].difcol=t[id<<1|1].difcol=0;
t[id<<1].lz=t[id<<1|1].lz=col;
t[id<<1].sz=mid-l+1;t[id<<1|1].sz=r-mid;
}
}
bool queryok(int x,int y,int z,int l,int r,int id){
if(l>=x&r<=y){
nowsz+=t[id].sz;
if(t[id].col!=z&&t[id].col!=0)return false;
return true;
}
int mid=l+r>>1;
pushdown(l,r,id);
int res=1;
if(x<=mid)res&=queryok(x,y,z,l,mid,id<<1);
if(y>mid)res&=queryok(x,y,z,mid+1,r,id<<1|1);
return res;
}
void update(int x,int y,int z,int l,int r,int id){
if(l>=x&&r<=y){
t[id].col=z;t[id].lz=z;t[id].difcol=0;t[id].sz=r-l+1;return ;
}
int mid=l+r>>1;
pushdown(l,r,id);
if(x<=mid)update(x,y,z,l,mid,id<<1);
if(y>mid)update(x,y,z,mid+1,r,id<<1|1);
pushup(id);
}
int query(int x,int l,int r,int id){
if(l==r){
return t[id].col;
}
int mid=l+r>>1;
pushdown(l,r,id);
if(x<=mid)return query(x,l,mid,id<<1);
else return query(x,mid+1,r,id<<1|1);
}
int query(int x,int y,int l,int r,int id){
if(l>=x&&r<=y){
nowsz+=t[id].sz;
return t[id].col;
}
int mid=l+r>>1;
pushdown(l,r,id);
int res1=-1,res2=-1;
if(x<=mid)res1=query(x,y,l,mid,id<<1);
if(y>mid)res2=query(x,y,mid+1,r,id<<1|1);
if(res1!=-1&&res2!=-1&&res1!=res2){
return 0;
}else if(res1==res2)return res1;
else if(res1!=-1)return res1;
else return res2;
}
void solve(){
int n,q;cin>>n>>q;
vector<vector<int> > lst;
vector<vector<string>> lst2;
for(int i=1;i<=q;i++){
vector<int> lstt;
vector<string> lstt2;
int op;cin>>op;lstt.push_back(op);
if(op==1){
int col;cin>>col;lstt.push_back(col);
string l,r;cin>>l>>r;
s.insert(l);s.insert(r);
lstt2.push_back(l);lstt2.push_back(r);
}else if(op==2){
string x;cin>>x;
s.insert(x);lstt2.push_back(x);
}else{
string l,r;cin>>l>>r;
s.insert(l);s.insert(r);
lstt2.push_back(l);lstt2.push_back(r);
}
lst.push_back(lstt);lst2.push_back(lstt2);
}
int cnt=0;
for(auto& v:s){
++cnt;mp[v]=++cnt;
}
int len=cnt;
for(int i=0;i<lst2.size();i++){
for(auto& s:lst2[i])lst[i].push_back(mp[s]);
}
for(int i=0;i<q;i++){
vector<int> lstt=lst[i];
int op=lstt[0];
if(op==1){
int col=lstt[1],l=lstt[2],r=lstt[3];
nowsz=0;
if(queryok(l,r,col,1,len,1)&&nowsz!=r-l+1){
cout<<"YES"<<endl;
//cout<<l<<" "<<r<<" "<<col;
update(l,r,col,1,len,1);
//cout<<" "<<t[1].col<<endl;
}else{
cout<<"NO"<<endl;
}
}else if(op==2){
int x=lstt[1];
int res=query(x,1,len,1);assert(res!=0x3f3f3f3f);
cout<<res<<endl;
}else{
nowsz=0;
int l=lstt[1],r=lstt[2];
int res=query(l,r,1,len,1);
if(res==0||res==-1||res==0x3f3f3f3f||nowsz!=r-l+1)cout<<0<<endl;
else cout<<res<<endl;
//cout<<"fk"<<nowsz<<endl;
}
}
}
signed main(){
int t=1;
if(multi)cin>>t;
while(t--)solve();
}
施肥 35分
不想做最后一题,反正也不是我能做出来的,所以就打了个暴力,水了35分,大体思路就是枚举l,r,然后再遍历一遍所有车子,车子在l,r范围内的就把那个区间加上1,最后查询区间最小值是不是大于0即可。后面了解到可以水到60分,大概思路也是枚举l,r,不是遍历一遍所有车子,而是使用双指针类似的方法动态的删减,然后线段树区间加。后悔没多想想,再加上需要返校考试,最后1h只能交卷赶车,所以没写。如果再特判性质A的那15分,可能会拿到75分。100分的解法想不出来。
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
int multi=false;
void solve(){
int n,m;cin>>n>>m;
vector<pair<int,int> >a;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
a.push_back({u,v});
}
int res=0;
for(int l=1;l<=n;l++){
for(int r=l;r<=n;r++){
vector<int> vis(n+1);
for(int i=1;i<=m;i++){
if(a[i-1].first>=l&&a[i-1].second<=r){
for(int x1=a[i-1].first;x1<=a[i-1].second;x1++)
vis[x1]=1;
}
}
int fg=1;
for(int i=l;i<=r;i++)if(!vis[i])fg=0;
if(fg)res++;
}
}
cout<<res<<endl;
}
signed main(){
int t=1;
if(multi)cin>>t;
while(t--)solve();
}