A题 New Year and Naming
题解:
当时第一思路是求出循环节就行了,不过处理的时候需要特判是n的倍数或是m的倍数的情况。
比赛完看了zzq的代码,真的简介,我们从0开始读入字符串,这样询问的时候直接模拟输出就行了。
代码实现:
#pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #define PI atan(1.0)*4 #define E 2.718281828 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int INF = 0x3f3f3f3f; string s1[27],s2[27]; string ans[100005]; int main(){ int n=read(),m=read(); rp(i,0,n-1) cin>>s1[i]; rp(i,0,m-1) cin>>s2[i]; int q=read(); while(q--){ int y=read();y--; cout<<s1[y%n]<<s2[y%m]<<endl; } return 0; }
B.New Year and Ascent Sequence
题解:
首先我们需要求出每一个序列的最小值和最大值,以及是否序列本身有ascent。
我们可以考虑如果一个序列的最大值大于另一个序列的最小值,那么就是符合的。
而且如果一个序列本身有ascent的,我们可以假设他的最小值为-1,最大值为INF,意味着无论这个序列和谁拼接都是符合的。
然后就有两种处理方法:前缀和或者二分。
第一种方法:我们可以维护一个最小值的前缀和pre,然后对每个序列而言,对答案的贡献就pre[MAX[i]],表示前面有多少个序列的最小值是小于当前序列的最大值(即符合条件的)。注意这里
第二种方法:我们可以对最大值进行排序,然后去二分大于当前最小值的最大值个数就行了。
前缀和代码实现:
#pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #define PI atan(1.0)*4 #define E 2.718281828 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int INF = 0x3f3f3f3f; const int N = 1e6+100; int pre[N],MIN[N],MAX[N]; int main(){ int n=read(); rp(i,1,n){ MIN[i]=INF; int flag=0; int l=read(); rp(j,1,l){ int x=read(); MIN[i]=min(x,MIN[i]); MAX[i]=max(x,MAX[i]); if(MIN[i]<x&&j!=1) flag=1; } if(flag) MIN[i]=-1,MAX[i]=N-1; pre[MIN[i]+1]++; } rp(i,1,N-1) pre[i]+=pre[i-1]; ll ans=0; for(int i=1;i<=n;i++) ans+=pre[MAX[i]]; cout<<ans<<endl; return 0; }
二分的代码实现(没写,copy的网上的代码)
#include<iostream> #include<vector> #include<cstring> #include<map> #include<algorithm> using namespace std; vector<int> MAX; vector<int> MIN; int main(){ int t; cin>>t; for(int i = 0;i<t;i++){ int Length; cin>>Length; long long Min = 999999999; long long Max = -1; int ok = 0; for(int j = 0;j<Length;j++){ long long cur; cin>>cur; if(cur> Min && ok == 0){ ok = 1; } Min = min(Min,cur); Max = max(Max,cur); } if(ok == 0){ MAX.push_back(Max); MIN.push_back(Min); } else{ MAX.push_back(9999999); MIN.push_back(-1); } } sort(MAX.begin() ,MAX.end() ); long long ans = 0; for(int i = 0;i<t;i++){ ans+=(MAX.end() -upper_bound(MAX.begin() ,MAX.end() ,MIN[i])); //cout<<ans<<endl; } cout<<ans; return 0; }
C题 New Year and Permutation
题解:
组合排列题,首先我们考虑长度为n的排列中,长度为k的framed segment满足长度为k的子串中最大值-最小值=k.
举个例子,长度为5的排列中,长度为3的framed-segment有123,234和345.然后我们考虑把这长度为k的排列看成一个整体。那么长度就缩小成了n-k+1,长度为k的framed-segment的排列有(n-k+1)中,每一种的排列有k!个,最后整体的排列为(n-k+1)!个。因此对答案的贡献为(n-k+1)*k!*(n-k+1)!。
因此只要我们预处理出阶乘就行了。
代码实现:
#pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #define PI atan(1.0)*4 #define E 2.718281828 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int INF = 0x3f3f3f3f; const int N = 250007; int a[N]; int main(){ int n=read(),m=read(); a[0]=1; rp(i,1,n) a[i]=1ll*a[i-1]*i%m; ll sum=0; rp(i,1,n) sum=(sum+1ll*(n-i+1)*a[i]%m*a[n-i+1]%m)%m; cout<<sum<<endl; return 0; }
D题New Year and Conference
详细题解:https://blog.csdn.net/qq_43472263/article/details/104276639
代码实现:
#pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #define PI atan(1.0)*4 #define E 2.718281828 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int INF = 0x3f3f3f3f; const int N = 1e5+7; struct node{ int sa,ea,sb,eb; node(){}; node(int sa,int ea,int sb,int eb):sa(sa),ea(ea),sb(sb),eb(eb){}; bool operator<(const node& others){ return sa==others.sa?ea<others.ea:sa<others.sa; } }p[N]; int MAX[N<<2],MIN[N<<2]; void pushup(int rt){ MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]); MIN[rt]=min(MIN[rt<<1],MIN[rt<<1|1]); } void build(int l,int r,int rt){ if(l==r){ MAX[rt]=p[l].sb; MIN[rt]=p[l].eb; return ; } int m=l+r>>1; build(lson); build(rson); pushup(rt); } int queryMIN(int l,int r,int rt,int ql,int qr){ if(ql<=l&&r<=qr) return MIN[rt]; int m=l+r>>1; if(qr<=m) return queryMIN(lson,ql,qr); else if(ql>=m) return queryMIN(rson,ql,qr); else return min(queryMIN(lson,ql,m),queryMIN(rson,m+1,qr)); } int queryMAX(int l,int r,int rt,int ql,int qr){ if(ql<=l&&r<=qr) return MAX[rt]; int m=l+r>>1; if(qr<=m) return queryMAX(lson,ql,qr); else if(ql>=m) return queryMAX(rson,ql,qr); else return max(queryMAX(lson,ql,m),queryMAX(rson,m+1,qr)); } bool solve(int n){ sort(p+1,p+1+n); build(1,n,1); rp(i,1,n){ int pos=lower_bound(p+1,p+1+n,node(p[i].ea,INF,0,0))-p-1; if(i+1>pos) continue; if(queryMIN(1,n,1,i+1,pos)<p[i].sb||queryMAX(1,n,1,i+1,pos)>p[i].eb) return false; } return true; } int main(){ int n=read(); rp(i,1,n) p[i].sa=read(),p[i].ea=read(),p[i].sb=read(),p[i].eb=read(); int cnt=0; if(solve(n)) cnt++; rp(i,1,n) swap(p[i].sa,p[i].sb),swap(p[i].ea,p[i].eb); if(solve(n)) cnt++; if(cnt==2) cout<<"Yes"<<endl; else cout<<"No"<<endl; return 0; }
E题 New Year and Castle Construction
题目大意:
给n个点,问存在多少个四个点围成的四边形(可能是凹四边形,这样围成的就是三角形)严格包围某个点。
不存在三点共线,支持复杂度O(n*n*logn)。
题解:
首先我们可以求出所有情况,然后减去那些四边形不包含某个点的情况。
这个题基于一个结论,如果我们任取两个点构成一条边,我们可以求出这条边上面的所有点,这样在这些点中任取三个点和组成边的那个点组成的四边形不包含组成边的另一个点。
首先我们可以枚举一个点,然后其他点与当前枚举的那个点进行极角排序,之后我们再枚举其他的所有点,按照上面那个结论求出答案,然后减去就行了。
注意精度问题,需要用long double来存储点和角度,表示PI的时候需要用acos(-1.0L),也是精度问题。
代码实现:
#pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #define E 2.718281828 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int INF = 0x3f3f3f3f; const int N = 2507; long double x[N],y[N],PI=acos(-1.0L); int main(){ int n; cin>>n; rp(i,0,n-1) cin>>x[i]>>y[i]; ll ans=1ll*n*(n-1)*(n-2)*(n-3)*(n-4)/24;//n个点选四个共有res种情况 rp(i,0,n-1){ vector<long double> v; rp(j,0,n-1){ if(i==j) continue; v.pb(atan2(y[j]-y[i],x[j]-x[i]));//求出相对于当前点的角度(极角) } sort(v.begin(),v.end());//极角排序 int size=n-1,index=0; rp(j,0,size-1){//枚举另一个点组成边 while(index<j+size){//统计在边上面的点的个数 long double ang=v[index%size]-v[j];//求出index点相对j点的角度,需要环形的枚举出当前边的两个点的所有点 if(ang<0) ang+=2*PI;//角度小于0的话再走一圈 if(ang<PI) index++; else break; //在下方的话就跳出 } ll cnt=index-j-1;//枚举到相对于j的角度大于等于180为止,共有index-j个点,不包括j,故再-1 ans-=1ll*cnt*(cnt-1)*(cnt-2)/6;//在这些点里面任取三个点的构成的答案即为不符合条件的,减去就行 } } cout<<ans<<endl; return 0; }