title: Dirt030的码题记录
date: 2019-10-06 16:52:45
updated:
tags: ACM
---
为了监督自己补题,顺便存代码,把码的题扔在Blog上吧。
Codeforces Round #601 (Div. 2)
A Changing Volume
传送门
题解
首先大于等于五格音量的肯定优先按+/-5
然后人工处理一下,四格音量要两次+/-2;三格音量一次+/-1,一次+/-2;两格和一格都是一次
因为忘了case的语法,所以使用了嵌套if
#include<bits/stdc++.h> #define ll long long using namespace std; int main(){ /*freopen("sample.in", "r", stdin); freopen("sample.out", "w", stdout);*/ int T; cin>>T; while(T--){ int a,b; cin>>a>>b; int ans=0; int d=abs(a-b)/5,m=abs(a-b)%5; if(m==0) ans=d; else if(m==1) ans=d+1; else if(m==2) ans=d+1; else if(m==3) ans=d+2; else if(m==4) ans=d+2; cout<<ans<<endl; } /*fclose(stdin); fclose(stdout);*/ return 0; }
B Fridge Lockers
传送门
题解
这道题当时好像出锅了,赛后开VP里面多了m≤n的限制。显然一个冰箱至少要和两个其他冰箱相连才能保证不会被别人打开,结合m≤n的限制,冰箱只能成环。特判一下n==2就行。
#include<bits/stdc++.h> #define ll long long using namespace std; int main(){ /*freopen("sample.in", "r", stdin); freopen("sample.out", "w", stdout);*/ int T; cin>>T; while(T--){ int n,m; cin>>n>>m; int ans=0,tmp; for(int i=0;i<n;i++){ cin>>tmp; ans+=tmp; } ans*=2; if(n==2||m<n) cout<<-1<<endl; else { cout<<ans<<endl; for(int i=1;i<n;i++) cout<<i<<' '<<i+1<<endl; cout<<n<<' '<<1<<endl; } } /*fclose(stdin); fclose(stdout);*/ return 0; }
C League of Leesins
传送门
题解
纯模拟,因为开头只出现过一次,所以找出来然后一个一个推过去就行。
懒得想变量名所以代码写的很丑陋。
#include<bits/stdc++.h> #define ll long long using namespace std; //tga记录每个三元组的三个数字,agt记录每个数字被包含于哪几个三元组 struct tgadata{ int num,q[3]; }tga[100005],agt[100005]; //枚举寻找同时包含b和c的三元组,然后如果其不含有a,那它就是下一个三元组 int findnxt(int a,int b, int c){ for(int i=0;i<agt[b].num;i++){ for(int j=0;j<agt[c].num;j++){ if(agt[b].q[i]==agt[c].q[j]){ for(int k=0;k<3;k++){ if(tga[agt[b].q[i]].q[k]!=b&&tga[agt[b].q[i]].q[k]!=c){ if(tga[agt[b].q[i]].q[k]!=a) return tga[agt[b].q[i]].q[k]; break; } } } } } return 0; } int main(){ /*freopen("sample.in", "r", stdin); freopen("sample.out", "w", stdout);*/ int n; cin>>n; for(int i=0;i<n-2;i++){ for(int j=0;j<3;j++){ cin>>tga[i].q[j]; agt[tga[i].q[j]].q[agt[tga[i].q[j]].num++]=i; } tga[i].num=i; } //这里因为懒得压代码所以直接分开来写的,实际上如果考虑到n>=5的限制,第二和第三个数有更简单的办法找出来 //我这里针对了不存在的n==3和n==4进行了处理 int p1; for(int i=0;i<n-2;i++){ if(agt[i].num==1){ p1=i; break; } } int p2; for(int i=0;i<3;i++) { if(tga[agt[p1].q[0]].q[i]!=p1&&agt[tga[agt[p1].q[0]].q[i]].num<=2){ p2=tga[agt[p1].q[0]].q[i]; break; } } int p3; for(int i=0;i<3;i++) { if(tga[agt[p1].q[0]].q[i]!=p1&&tga[agt[p1].q[0]].q[i]!=p2){ p3=tga[agt[p1].q[0]].q[i]; break; } } cout<<p1; for(int i=0;i<n-3;i++){ int tmp=findnxt(p1,p2,p3); p1=p2; p2=p3; p3=tmp; cout<<' '<<p1; } cout<<' '<<p2<<' '<<p3<<endl; /*fclose(stdin); fclose(stdout);*/ return 0; }
Hello 2020
A New Year and Naming
传送门
题解
类似中国的天干地支纪年法,将y分别对n和m取模然后对应到名字即可。
#include<bits/stdc++.h> #define ll long long using namespace std; int main(){ /*freopen("sample.in", "r", stdin); freopen("sample.out", "w", stdout);*/ int n,m; cin>>n>>m; string a[23],b[23]; for(int i=0;i<n;i++){ cin>>a[i]; } for(int i=0;i<m;i++){ cin>>b[i]; } int q; cin>>q; int y; while(q--){ cin>>y; string ans=a[(y-1)%n]+b[(y-1)%m]+"\n"; cout<<ans; } /*fclose(stdin); fclose(stdout);*/ return 0; }
B New Year and Ascent Sequence
传送门
题解
将序列分为两类,一种是本身存在上升数对的,记有a个;一种是本身不存在上升数对的,记有b个。
对于前一种和前一种排列,任意排列均可,因此有a*a个可行解;对于前一种和后一种排列,任意排列均可,因此有a*b个可行解;对于后一种和后一种排列,需要满足前一个序列的最小值小于或等于后一个的最大值,这个需要枚举计算。
对于计算第三类排列,考虑到我们并不关心到底是谁和谁排列,只关心能排列出多少种,因此可以只保留每个序列的最大和最小值这一信息。接下来,先将最大和最小值数列分别升序排列,注意到可以利用数列的单调性降低枚举复杂度(暴力为O(n^2^))。对于某个特定的最小值,我们找到了一个刚好大于它的最大值,那么比这个值更大的均可以成为可行解;而比当前选取的最小值更大的最小值,能与其形成可行解的最大值必然在之前选取的最大值之后。通过单调性,我们只需要将序列枚举一遍,复杂度降为O(n)。
比赛时由于没考虑到第一类可行解,导致在debug的时候缝缝补补,代码很丑陋,变量名也在乱来。
#include<bits/stdc++.h> #define ll long long using namespace std; int smin[100005],smax[100005]; int main(){ /*freopen("sample.in", "r", stdin); freopen("sample.out", "w", stdout);*/ ll n; cin>>n; //nn为第一种序列,nnn为第二种序列 ll nn=0,nnn=0; for(int j=0;j<n;j++){ int l; cin>>l; bool flg=1; int minn=1000006,maxx=-1; for(int i=0;i<l;i++){ int tmp; cin>>tmp; //判断是否自身存在上升数对 if(flg&&tmp>minn){ nnn++; flg=0; } minn=min(tmp,minn); maxx=max(tmp,maxx); } if(flg){ smin[nn]=minn; smax[nn]=maxx; nn++; } }; //cout<<nn<<' '<<nnn<<endl; sort(smin,smin+nn); sort(smax,smax+nn); //for(int i=0;i<nn;i++) cout<<smax[i]<<' ';cout<<endl; //for(int i=0;i<nn;i++) cout<<smin[i]<<' ';cout<<endl; ll ans=0; //计算第三类排列 ll p1=0,p2=0; while(p1!=nn){ while(p1!=nn&&p2!=nn&&smin[p1]>=smax[p2]){ p2++; } ans+=nn-p2; p1++; } //计算前两类排列 ans+=nnn*nn*2+nnn*nnn; cout<<ans<<"\n"; /*fclose(stdin); fclose(stdout);*/ return 0; }
C New Year and Permutation
传送门
题解
首先不能把思路放在对于特定的数列怎么求其满足题意的子序列数量,而是直接思考如何构造满足题意的子序列。
因为r-l正好是max-min,而每个数又是不一样的,所以显然l到r这个区间里的数也必须正好是连续的。
我们考虑这段连续的数字的个数是i,那么这段数字有n-i+1种取法,其排列方式有 i! 种,而对于剩下的n-i个数字,其排列在选出的这段数字区间两端,首先,其排列有 (n-i)! 种,然后用插板法将其分为两份放在选定数字区间的两侧,因此还要再乘以(n-i+1)。
因此最终答案为 Σni=1( i! * (n-i+1) * (n-i+1)! )。
#include<bits/stdc++.h> #define ll long long using namespace std; ll jc[250004]; int main(){ /*freopen("sample.in", "r", stdin); freopen("sample.out", "w", stdout);*/ ll n,m; cin>>n>>m; //预处理阶乘的值 jc[0]=1; for(ll i=1;i<=n;i++){ jc[i]=jc[i-1]*i%m; } //计算公式 ll ans=0; for(ll i=1;i<=n;i++){ //ans=(ans+(((jc[i]*jc[n-i])%m)*(((n-i+1)*(n-i+1)))%m)%m)%m; ans=(ans+(jc[i]*jc[n-i+1]%m)*(n-i+1)%m)%m; } cout<<ans<<endl; /*fclose(stdin); fclose(stdout);*/ return 0; }
D New Year and Conference
传送门
题解
一共有两种NO的情况,分别是两个演讲在A场地冲突,在B场地不冲突;在B场地冲突,在A场地不冲突。所以需要判断两次,但其实判断流程是一样的。
那前一种NO的情况做例子,我们先将每场演讲在A场地的开始和结束的时间节点按照时间顺序排列,同时将演讲的序号也记录进去。有点像括号匹配,我们顺序枚举每个时间节点,用一个Set记录当前有哪些演讲在讲,然后每当碰到一个时间节点具有未出现的演讲序号,就加进去,碰到已出现的就删除Set里的对应的演讲。这就意味着,同时出现在Set中的演讲序号其对应的演讲在A场地是冲突的。
那么如何处理这些演讲是否在B场地也冲突呢,我们用一个线段树维护每个时间节点有多少演讲,即每当Set中加入一场演讲,我们就把其在B场地演讲的时间段包含的时间节点全部加一,每当删除就全部减一。因为演讲只要在一个节点全部冲突,那么就是全部冲突,所以我们只要询问当前线段树所有节点的最大值,如果最大值等于Set中的演讲数量,那么他们必然在B中至少有一个时间节点全部冲突,否则不存在一个全部冲突的时间节点。这样就可以解决Set中演讲是不是同时在B场地全部冲突。区间修改,区间查询最值的线段树是模板,不再赘述。
由于数值的范围,需要对数据离散化,这也是为什么前文中使用的词语是时间节点。
#include<bits/stdc++.h> using namespace std; //每场演讲的属性 struct cfdata{ int sa,ea,sb,eb; }cf[100005]; //演讲的开始和结束的时间节点和对应的演讲序号 pair<int,int> cfina[200005],cfinb[200005]; //按照时间顺序排序 bool cmp(pair<int,int> a,pair<int,int> b){ return a.second<b.second; } //离散化 vector<int> trans; int gettrans(int x){ return lower_bound(trans.begin(),trans.end(),x)-trans.begin()+1; } //线段树,因为询问全局最大值就是询问f[1],所以没有query() //而初始就是空树,所以也没有build() int f[2001005],tag[2001005]; void downtag(int x){ if(tag[x]==0) return; tag[x*2]+=tag[x]; f[x*2]+=tag[x]; tag[x*2+1]+=tag[x]; f[x*2+1]+=tag[x]; tag[x]=0; } void modify(int x,int l,int r,int ll,int rr,int d){ if(ll<=l&&r<=rr){ f[x]+=d; tag[x]+=d; } else{ downtag(x); int mid=(l+r)/2; if(ll<=mid) modify(x*2,l,mid,ll,rr,d); if(rr>=mid+1) modify(x*2+1,mid+1,r,ll,rr,d); f[x]=max(f[x*2],f[x*2+1]); } } int main(){ /*freopen("sample.in", "r", stdin); freopen("sample.out", "w", stdout);*/ int n; cin>>n; //输入数据并复制到对应的记录表中 for(int i=0;i<n;i++){ cin>>cf[i].sa>>cf[i].ea>>cf[i].sb>>cf[i].eb; trans.push_back(cf[i].sa); trans.push_back(cf[i].ea); trans.push_back(cf[i].sb); trans.push_back(cf[i].eb); cfina[i]=make_pair(i,cf[i].sa); cfina[n+i]=make_pair(i,cf[i].ea); cfinb[i]=make_pair(i,cf[i].sb); cfinb[n+i]=make_pair(i,cf[i].eb); } //离散化 sort(trans.begin(),trans.end()); trans.erase(unique(trans.begin(),trans.end()),trans.end()); //按时间顺序排列 sort(cfina,cfina+n*2,cmp); sort(cfinb,cfinb+n*2,cmp); //for(int i=0;i<n*2;i++) cout<<cfina[i].first<<' ';cout<<endl; //记录当前时间节点有哪些演讲 set<int> cfinnow; //build() memset(f,0,sizeof(f)); memset(tag,0,sizeof(tag)); for(int i=0;i<n*2;i++){ //判断当前时间节点是演讲的开始节点还是结束节点 //如果是结束节点,那么对应的演讲序号应该已在Set中存在,反之亦然 if(cfinnow.find(cfina[i].first)!=cfinnow.end()){ //在A场地和B场地都删除当前演讲 cfinnow.erase(cfina[i].first); modify(1,1,trans.size(),gettrans(cf[cfina[i].first].sb),gettrans(cf[cfina[i].first].eb),-1); }else { //在A场地和B场地均加入当前演讲 cfinnow.insert(cfina[i].first); modify(1,1,trans.size(),gettrans(cf[cfina[i].first].sb),gettrans(cf[cfina[i].first].eb),1); //判断当前所有演讲是否在两个场地均冲突 if(f[1]!=cfinnow.size()){ cout<<"NO"<<endl; return 0; } } } //再做一次B场地冲突是不是A场地也冲突 cfinnow.clear(); memset(f,0,sizeof(f)); memset(tag,0,sizeof(tag)); for(int i=0;i<n*2;i++){ if(cfinnow.find(cfinb[i].first)!=cfinnow.end()){ cfinnow.erase(cfinb[i].first); modify(1,1,trans.size(),gettrans(cf[cfinb[i].first].sa),gettrans(cf[cfinb[i].first].ea),-1); }else { cfinnow.insert(cfinb[i].first); modify(1,1,trans.size(),gettrans(cf[cfinb[i].first].sa),gettrans(cf[cfinb[i].first].ea),1); if(f[1]!=cfinnow.size()){ cout<<"NO"<<endl; return 0; } } } cout<<"YES"<<endl; /*fclose(stdin); fclose(stdout);*/ return 0; }
Codeforces Round #590 (Div. 3)
D Distinct Characters Queries
传送门
题解
用26个BIT维护一下。
然而比赛的时候给B题整自闭了(循环写成i--,结果一直找不到哪儿错了),最后被忽悠说一定要用线段树就没做。
补题的时候还把memset()的参数顺序搞错了,又查了半天(VSCode把我养成five了,参数次次记不得)。
#include<bits/stdc++.h> #define ll long long using namespace std; int f[100005][30]; int lowbit(int x){ return x&(-x); } void modify(int x,int c,int d,int len){ while(x<=len){ f[x][c]+=d; x+=lowbit(x); } return; } void query(int x,int *tf){ while(x!=0){ for(int i=0;i<26;i++){ tf[i]+=f[x][i]; } x-=lowbit(x); } return; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); char s[100005]; cin>>s; int len=strlen(s); memset(f,0,sizeof(f)); for(int i=0;i<len;i++){ modify(i+1,s[i]-'a',1,len); } int n; cin>>n; int t,p,l,r; char ch; int tfl[30],tfr[30]; int ans; for(int i=0;i<n;i++){ /*cout<<s<<endl; for(int k=1;k<=len;k++){ cout<<k<<' '<<lowbit(k)<<endl; for(int j=0;j<26;j++){ cout<<f[k][j]<<" "; } cout<<endl; }*/ cin>>t; if(t==1){ cin>>p>>ch; modify(p,s[p-1]-'a',-1,len); s[p-1]=ch; modify(p,s[p-1]-'a',1,len); } else{ cin>>l>>r; memset(tfl,0,sizeof(tfl)); query(l-1,tfl); memset(tfr,0,sizeof(tfr)); query(r,tfr); ans=0; /*for(int j=0;j<26;j++){ cout<<tfl[j]<<' '; } cout<<endl; for(int j=0;j<26;j++){ cout<<tfr[j]<<' '; } cout<<endl;*/ for(int j=0;j<26;j++){ if(tfl[j]!=tfr[j]){ ans++; } } cout<<ans<<endl; } } //fclose(stdin); //fclose(stdout); }
来源:https://www.cnblogs.com/Dirt030/p/12178011.html