A. New Year and Naming
分别取模。
#include <bits/stdc++.h>
using namespace std;
char ss[25][20],tt[25][20];
int main()
{
int n,m,q,k;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;i<n;i++)
scanf("%s",ss[i]);
for(int i=0;i<m;i++)
scanf("%s",tt[i]);
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%d",&k);
printf("%s%s\n",ss[(k-1)%n],tt[(k-1)%m]);
}
}
return 0;
}
B. New Year and Ascent Sequence
对于一些序列,本身就可以满足条件。所有无论把它方在哪都可以满足。因此这些序列的对应的种类可以直接算出。
而对于另一些序列,必须放置某些特定的序列的前面或后面 ,对于这些序列。可以把它的最大和最小值分别放在最大值和最小值数组中,然后遍历最大值数组,在最小值数组中用lower_bound(),即可求出其对应的方案数。
在long long上WA了一发。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N],b[N];
int main()
{
int n,l;
while(scanf("%d",&n)!=EOF)
{
int minn,maxn;
int cnt=0,cot=0;
long long ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&l);
int x,y,f=0;
minn=10000000;
maxn=-1;
scanf("%d",&y);
if(y>maxn)
maxn=y;
if(y<minn)
minn=y;
for(int j=2;j<=l;j++)
{
scanf("%d",&x);
if(x>maxn)
maxn=x;
if(x<minn)
minn=x;
if(x>y)
f=1;
y=x;
}
if(!f)
{
a[++cnt]=minn;
b[cnt]=maxn;
}
else
{
ans+=(2*n-2*cot-1);
cot++;
}
}
//cout<<"ans="<<ans<<endl;
sort(a+1,a+1+cnt);
sort(b+1,b+1+cnt);
for(int i=1;i<=cnt;i++)
{
int t=lower_bound(a+1,a+1+cnt,b[i])-a-1;
ans+=t;//cout<<"t="<<t<<endl;
}
printf("%lld\n",ans);
}
return 0;
}
C. New Year and Permutation
一开始没有想到往排列组合这方面想,一直以为要找规律来推公式。
根据排列组合推导公式为:
对于r-l=i的序列其合理的排列个数为:(n-i) * (i+1) * (n-i)!
预处理一下全排列,然后循环一下即可。
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+6e4;
typedef long long ll;
ll a[N];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
ll ans=0;
a[0]=1;
for(int i=1;i<=n;i++)
a[i]=(a[i-1]*(i%m))%m;
for(int i=0;i<n;i++)
ans=(ans+(n-i)*(a[n-i]*a[i+1]%m))%m;
printf("%lld\n",ans%m);
}
return 0;
}
【好题】D. New Year and Conference
题意大概:
对于任意两个事件如果在一个一种情况下是交叉的,那么在另一个情况下必定相交。
如果不符合,输出NO;否则,输出YES。
方法1:
基本思路:
以一个情况下的当前区间交叉情况为基准,同时判断另一个是否相同,一直到全部判断完。
采用stld的multiset维护。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int sa[N],ea[N],sb[N],eb[N];
int n;
struct node
{
int t,s,e,is_in;//t:表示在另一个区间中的开始时间和结束时间,s,e分别表示在当前情况下的开始和结束时间,is_in表示该区间初始一个放进判断范围还是拿出。
};
bool cmp(node x,node y)
{
if(x.t==y.t)
return x.is_in<y.is_in;//
return x.t<y.t;//排序时按,在另一种情况中的开始,结束时间为优先,
}
bool check(int a[],int b[],int c[],int d[])
{
vector<node>lecture;
multiset<int>ss,ee;//
for(int i=1;i<=n;i++)
{//把一个区间在另一个情况下的开始,结束拆开,便于随时维护
lecture.push_back({a[i],c[i],d[i],1});
lecture.push_back({b[i]+1,c[i],d[i],0});//注意+1
}
sort(lecture.begin(),lecture.end(),cmp);
for(int i=0;i<lecture.size();i++)
{//cout<<lecture[i].s<<" "<<lecture[i].e<<endl;
if(lecture[i].is_in)//如果该放入
{
if(!ss.empty())
{
int smax=*ss.rbegin();//最大的开始时间
int emin=*ee.begin();//最小的结束时间
if(lecture[i].s>emin||lecture[i].e<smax)
return false;//没有和所有的区间交叉
}
ss.insert(lecture[i].s);//插入
ee.insert(lecture[i].e);
}
else//该取出
{
ss.erase(ss.find(lecture[i].s));
ee.erase(ee.find(lecture[i].e));
}
}
return true;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
scanf("%d%d%d%d",&sa[i],&ea[i],&sb[i],&eb[i]);
if(check(sa,ea,sb,eb)&&check(sb,eb,sa,ea))
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
方法2:
基本思想和方法1相同,不同的是此处采用线段树维护,关键点在于对时间点进行枚举。
好久没有写线段树的我,写起来还是犯了不少错 >_<。
#include <bits/stdc++.h>
using namespace std;
const int N=4e5+5;//注意数组大小
int tree[N<<2],lazy[N<<2];
int n,len;
struct node
{
int l,r,num;
}a[N],b[N];
vector<int>all,tp1[N],tp2[N];
void build(int l,int r,int rt)
{
if(l==r)
{
tree[rt]=0;
lazy[rt]=0;
return;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
tree[rt]=0;
lazy[rt]=0;
}
void pushdown(int rt)
{
if(lazy[rt])
{
tree[rt<<1]+=lazy[rt];
tree[rt<<1|1]+=lazy[rt];
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
lazy[rt]=0;
}
}
void update(int L,int R,int val,int l,int r,int rt)
{
if(L<=l&&R>=r)
{
tree[rt]+=val;
lazy[rt]+=val;
return;
}
int mid=(l+r)>>1;
pushdown(rt);
if(L<=mid)
update(L,R,val,l,mid,rt<<1);
if(R>mid)
update(L,R,val,mid+1,r,rt<<1|1);
tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r)
return tree[rt];
int mid=(l+r)>>1;
int ans=0;
pushdown(rt);
if(L<=mid)
ans=max(ans,query(L,R,l,mid,rt<<1));
if(R>mid)
ans=max(ans,query(L,R,mid+1,r,rt<<1|1));
return ans;
}
bool check(node x[],node y[])
{
for(int i=0;i<N;i++)//巧妙之处,两个邻接表的使用
{
tp1[i].clear();
tp2[i].clear();
}
int cnt=0;//存当前判断时,x的交叉的区间个数,与用线段树维护的y的当前交叉的区间个数比较
build(1,len,1);
for(int i=1;i<=n;i++)
{
tp1[x[i].l].push_back(i);//存该时间点对应的时间段的开始时间
tp2[x[i].r].push_back(i);//存该时间点对应的时间段的开始时间
}
for(int i=1;i<=len;i++)//枚举时间点,关键
{
for(int j=0;j<tp1[i].size();j++)//开始时间
{
int id=x[tp1[i][j]].num;//找到此点对应的编号
int ans=query(y[id].l,y[id].r,1,len,1);//查询最大值
if(ans!=cnt)
return false;
update(y[id].l,y[id].r,1,1,len,1);//插入区间
cnt++;
}
for(int j=0;j<tp2[i].size();j++)//结束时间
{
int id=x[tp2[i][j]].num;
update(y[id].l,y[id].r,-1,1,len,1);//删除区间
cnt--;
}
}
return true;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
all.clear();
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&a[i].l,&a[i].r,&b[i].l,&b[i].r);
all.push_back(a[i].l);
all.push_back(a[i].r);
all.push_back(b[i].l);
all.push_back(b[i].r);
a[i].num=i;
b[i].num=i;
}//离散化:
sort(all.begin(),all.end());//排序
len=unique(all.begin(),all.end())-all.begin();//去重,len为有效的数据的长度
for(int i=1;i<=n;i++)//二分查找
{
a[i].l=lower_bound(all.begin(),all.begin()+len,a[i].l)-all.begin()+1;//线段树维护区间从1开始
a[i].r=lower_bound(all.begin(),all.begin()+len,a[i].r)-all.begin()+1;
b[i].l=lower_bound(all.begin(),all.begin()+len,b[i].l)-all.begin()+1;
b[i].r=lower_bound(all.begin(),all.begin()+len,b[i].r)-all.begin()+1;
}
if(check(a,b)&&check(b,a))//两次判断
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
持续更新。。。
2020继续加油!