Hello 2020
A. New Year and Naming (CF 1284 A)
题目大意
给定两个个数为\(n,m\)的字符串数组以及年份\(y\),将年份\(y\)编码,第一部分来自第一个字符串数组的第\((y-1)\%n\),第二部分来自第二个字符串数组的第\((y-1)\%m\)。
解题思路
主要就是读题的速度,能够再巨长文字中找到题目关键。
神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
int main(void) {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
/* int kase; read(kase);
for (int i = 1; i <= kase; i++) {
printf("Case #%d: ", i);
} */
int n,m;
cin>>n>>m;
string a[n],b[m];
for(int i=0;i<n;++i)
cin>>a[i];
for(int i=0;i<m;++i)
cin>>b[i];
int t;
cin>>t;
while(t--){
int year;
cin>>year;
--year;
cout<<a[year%n]<<b[year%m]<<endl;
}
return 0;
}
B. New Year and Ascent Sequence (CF 1284 B)
题目大意
给定\(n\)个数字串,先把两串(可以是同一个)连接起来得到一个串,若这个串不是递减的,则这是个好串。问好串的数量。
解题思路
(一开始看错题看成两个串的序号必须是第一个小于第二个的……然后写了\(BIT\)不仅\(WA\)了还\(T\)了\(qwq\))
对于一个本身是好串,它与任何串连接起来都是好串。而本身不能不是好串,连接起来是好串的,则需要第一个串的最小值要小于第二个串的最大值。
于是我们统计本身是好串的数量\(cnt\),然后把不是好串的串的最大值记录起来。枚举每一个串作为第一部分,考虑它对答案的贡献。
如果这是个好串那它对答案的贡献为\(cnt\)。如果不是好串,则它对答案的贡献就是最大值大于这个串最小值的串的个数。
对应代码注释部分。
我们还可以反过来考虑,即考虑哪些不符合要求。
我们要考虑的即是那些非严格递减的数字串,对于这样的一个数字串\(i\)作为第一部分,不符合题目要求的第二部分的数量则是最大值小于等于串\(i\)最小值的串的数量。
于是我们把非严格递减的数字串的最大值最小值丢到数组里排个序从小到大扫一遍统计最大值数量然后遇到一个最小值则减去已经统计的最大值的数量即可。
这样代码就简洁一点。
神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
int main(void) {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int n;
cin>>n;
vector<pair<int,int>> cnt;
for(int i=0;i<n;++i){
int l;
cin>>l;
vector<int> num(l);
for(int j=0;j<l;++j) cin>>num[j];
bool qwq=true;
for(int j=0;j<l-1;++j)
qwq&=(num[j]>=num[j+1]);
if (qwq) {
cnt.push_back(make_pair(num.front(),-1));
cnt.push_back(make_pair(num.back(),1));
}
}
sort(cnt.begin(),cnt.end(),less<pair<int,int>>());
int tmp=0;
LL ans=(LL)n*n;
for(auto i:cnt){
if (i.second==-1) ++tmp;
else ans-=tmp;
}
/* vector<int> minn(n,0),maxx(n,0);
vector<bool> sign(n,false);
int mm=0;
int cnt=0;
for(int l,i=0;i<n;++i){
cin>>l;
bool qwq=false;
int u;
cin>>u;
int mi=u,ma=u;
for(int v,j=1;j<l;++j){
cin>>v;
if (u<v) qwq=true;
u=v;
mi=MIN(mi,v);
ma=MAX(ma,v);
}
if (qwq) {sign[i]=qwq; ++cnt;}
minn[i]=mi;
maxx[i]=ma;
mm=MAX(mm,ma);
}
vector<int> qwq(mm+6);
for(int i=0;i<n;++i) if (!sign[i]) ++qwq[maxx[i]];
for(int i=1;i<=mm;++i) qwq[i]+=qwq[i-1];
LL ans=0;
for(int i=0;i<n;++i){
if (sign[i]) ans+=(LL)n;
else{
ans+=(LL)qwq[mm]-(LL)qwq[minn[i]];
ans+=(LL)cnt;
}
} */
cout<<ans<<endl;
return 0;
}
C. New Year and Permutation (CF 1284 C)
题目大意
如果一个序列\([l,r]\),有\(\max\limits_{a_i \in [l,r]}(a_i)-\min\limits_{a_i \in [l,r]}(a_i)=r-l\),则这是个好的序列。一个排列的开心度就是它子序列是好的序列的数量。求关于\(n\)的\(n!\)个排列中,所有排列的开心度的和。
解题思路
暴力求一个排列的所有好的序列显然会超时。我们考虑每个序列对答案的贡献。
从好的序列的定义我们可以发现一个重要性质就是这个好的序列一定是一串连续的数。那么我们枚举序列的长度,考虑它对答案的贡献。
假设我们考虑一个好的序列的长度是\(len\),不考虑顺序,这样的序列有\((n-len+1)\)个,它们的排列方式有\(len!\)种,可以放的位置有\((n-len+1)\)个,剩下\(n-len\)个数,它们的排列方式有\((n-len)!\)个,于是长度为\(len\)的好的序列对答案的贡献就是\((n-len+1)^2\times n!\times (n-len)!\)。最终的答案就是所有的长度的贡献和\(\sum\limits_{len=1}^n((n-len+1)^2\times n!\times (n-len)!)\)。
神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
int main(void) {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int n;
LL m;
cin>>n>>m;
vector<LL> jie(n+2);
jie[0]=jie[1]=1;
for(int i=2;i<=n;++i)
jie[i]=jie[i-1]*(LL)i%m;
LL ans=0;
for(int i=1;i<=n;++i)
ans=(ans+jie[n-i]*jie[i]%m*(LL)(n-i+1)%m*(LL)(n-i+1)%m)%m;
cout<<ans<<endl;
return 0;
}
D. New Year and Conference (CF 1284 D)
题目大意
给定\(n\)条线段,它们在\(A\)中覆盖了一个区间,在\(B\)中覆盖了另一个区间,问是否存在一个线段子集,使得这些线段在一个区间内存在交叉的情况(端点值相同也算,即\(\max(l_i,l_j)\geq \min(r_i,r_j)\)),存在输出\(NO\),不存在输出\(YES\)。
解题思路
经过分析可以发现,出现交叉情况的子集的线段元素一定可以化为2个。我们尝试去构造这样的子集。
我们先去构造在\(A\)中有交叉而在\(B\)中无交叉的子集。我们对线段在\(A\)区域的左端点进行从小到大排序,然后倒序枚举线段\(i\),它的左右端点分别为\(sa_i,ea_i\),那么另一个会与线段\(i\)交叉的线段\(j\)满足\(sa_j\leq ea_i\),而此线段\(j\)在\(B\)中与\(i\)不能有交叉,则要满足\(eb_j<sb_i\)或\(sb_j>eb_i\)。综合起来即是
三维偏序问题CDQ分治,不过这题并不是统计数量,而是可行性的判断。只要\(\min\limits_{sa_j \in [sa_i,ea_i]}(eb_j)<sb_i\)或\(\max\limits_{sa_j \in [sa_i,ea_i]}(sb_j)>eb_i\),那么我们就找到了那个子集。于是我们用线段树储存\(sa\)在区间\([sa_i,ea_i]\)的\(sb\)最大值和\(eb\)最小值即可。但注意到端点值可能到\(10^9\),但最多只有\(2*10^5\)个不同的端点值,我们一开始离散化下点坐标即可。
然后如果没有找到对应的子集那再构造\(A\)无交叉而在\(B\)无交叉即可。
神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
#define lson root<<1
#define rson root<<1|1
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
const int N=2e6+8;
struct data{
pair<int,int> ji[N];
void build(int root,int l,int r){
if (l==r) {
ji[root]=make_pair(0,1e9+7);
return;
}
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
ji[root]=make_pair(0,1e9+7);
return;
}
pair<int,int> query(int root,int l,int r,int ll,int rr){
if (ll<=l&&rr>=r) return ji[root];
int mid=(l+r)>>1;
pair<int,int> ta,tb,tc;
tb.first=ta.first=-1;
tb.second=ta.second=1e9+7;
if (ll<=mid) ta=query(lson,l,mid,ll,rr);
if (rr>mid) tb=query(rson,mid+1,r,ll,rr);
tc.first=MAX(tb.first,ta.first);
tc.second=MIN(tb.second,ta.second);
return tc;
}
void insert(int root,int l,int r,int id,pair<int,int> x){
if (l==r){
ji[root].first=MAX(ji[root].first,x.first);
ji[root].second=MIN(ji[root].second,x.second);
return;
}
int mid=(l+r)>>1;
if (id<=mid) insert(lson,l,mid,id,x);
else insert(rson,mid+1,r,id,x);
ji[root].first=MAX(ji[lson].first,ji[rson].first);
ji[root].second=MIN(ji[lson].second,ji[rson].second);
}
}Segment;
void work(int n,int cnt,unordered_map<int,int>&qaq,vector<pair<pair<int,int>,pair<int,int>>> &a,bool &qvq){
Segment.build(1,1,cnt);
for(int i=n-1;i>=0;--i){
pair<int,int> tmp=Segment.query(1,1,cnt,qaq[a[i].first.first],qaq[a[i].first.second]);
if (tmp.first!=0&&(tmp.second<a[i].second.first||tmp.first>a[i].second.second)){
qvq=true;
break;
}
Segment.insert(1,1,cnt,qaq[a[i].first.first],a[i].second);
}
}
int main(void) {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int n;
cin>>n;
set<int> qaq,qbq;
qaq.clear();
qbq.clear();
vector<pair<pair<int,int>,pair<int,int>>> a,b;
for(int u,v,uu,vv,i=0;i<n;++i){
cin>>u>>v>>uu>>vv;
a.push_back(make_pair(make_pair(u,v),make_pair(uu,vv)));
b.push_back(make_pair(make_pair(uu,vv),make_pair(u,v)));
qaq.insert(u);
qaq.insert(v);
qbq.insert(uu);
qbq.insert(vv);
}
unordered_map<int,int> hasha,hashb;
hasha.clear();
hashb.clear();
int cnta=0,cntb=0;
for(auto i:qaq) hasha[i]=++cnta;
for(auto i:qbq) hashb[i]=++cntb;
sort(a.begin(),a.end(),less<pair<pair<int,int>,pair<int,int>>>());
sort(b.begin(),b.end(),less<pair<pair<int,int>,pair<int,int>>>());
bool qvq=false;
work(n,cnta,hasha,a,qvq);
if (!qvq) work(n,cntb,hashb,b,qvq);
if (qvq) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
return 0;
}