Hello 2020 題解


新年第一場。

開場 2min 寫了 A,再 5min 寫了 B,再 4min 寫了 C。

然后由於腦子一抽,沒想清楚就開始碼 D,花了 18min 寫完。

然后發現好友列表里的人好像切四題的不多,還能玩。

然后開始想 E。

一開始先去問了個是否必須是凸多邊形和一個組成凹多邊形(會有三種情況)的點集要被算多少次。

看到三種情況只能算一次,心態就崩了……

5min 后才發現如果合法,恰好一種是合法的,白白浪費了一堆時間……

然后開碼。比想象中的好碼。但由於計算幾何,所以還是用了很長時間,大概 1h。

然后 WA9。

對拍也不太想寫,就靜態查錯,死都查不出來。

棄療了,水群去了。

……

然后天真的以為 long double 就夠了,又送一發罰時。

最后還是重構成了叉積+判象限的寫法,終於在 2:09 過掉了。

E 的得分比 C 還少。

看起來發揮還不錯,上 IM 了。

(戲劇性的一幕:tourist 最后 2min 切了 G 翻上了 rk1,predictor 顯示 dls 將仍是榜二,然后 tourist 的 G FST 了)

(祝賀 dls 登基,今年是 MiFaFaOvO 元年)


A

入門模擬。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=22;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,m,q;
char s[maxn][maxn],t[maxn][maxn];
int main(){
	n=read();m=read();
	FOR(i,0,n-1) scanf("%s",s[i]+1);
	FOR(i,0,m-1) scanf("%s",t[i]+1);
	q=read();
	while(q--){
		int y=read()-1;
		printf("%s%s\n",s[y%n]+1,t[y%m]+1);
	}
}

B

怎么跟我自己出的一題有點像……

反過來計算不合法的方案數,也就是不升序列的個數。

考慮選了 \(i,j\) 拼在一起,那么要 \(i,j\) 都是不升序列,而且 \(last_i\ge first_j\)

把不升序列拎出來,枚舉 \(i\),計算 \(\le last_i\)\(first_j\) 有多少個。可以前綴和或者二分。

時間復雜度 \(O(n\log n)\)\(O(n+\max(a))\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=1000100;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,l,a[maxn],s[maxn],t[maxn],m,cnt[maxn];
ll ans;
int main(){
	n=read();
	FOR(i,1,n){
		l=read();
		FOR(j,1,l) a[j]=read();
		bool flag=false;
		FOR(j,2,l) flag|=a[j-1]<a[j];
		if(!flag) cnt[a[1]]++,t[++m]=a[l];
	}
	FOR(i,1,1000000) cnt[i]+=cnt[i-1];
	ans=1ll*n*n;
	FOR(i,1,m) ans-=cnt[t[i]];
	printf("%lld\n",ans);
}

C

考慮 \([l,r]\) 作為連續段出現的次數。

這些數可以隨便排,\((r-l+1)!\)

剩下的數可以隨便排,\((n-(r-l+1))!\)

選擇連續段的開始位置,\(n-(r-l+1)+1\)

發現對於長度相同的 \([l,r]\) 貢獻一樣。枚舉長度,答案就是 \(\displaystyle\sum_{i=1}^ni!(n-i)!(n-i+1)^2\)

時間復雜度 \(O(n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=250025;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,mod,ans,fac[maxn];
int main(){
	n=read();mod=read();
	fac[0]=1;
	FOR(i,1,n) fac[i]=1ll*fac[i-1]*i%mod;
	FOR(i,1,n) ans=(ans+1ll*fac[n-i]*fac[i]%mod*(n-i+1)%mod*(n-i+1))%mod;
	printf("%d\n",ans); 
}

D

發現只需要考慮大小為 \(2\) 的子集。

因為如果存在大小為 \(2\) 的子集不滿足條件,那就是不滿足條件;如果全部大小為 \(2\) 的子集都滿足條件,那么很明顯整個也滿足條件。

問題就變成:對於任意兩個(不知道什么東西),要么它們的兩種區間同時有交,要么同時沒有交。

接下來大概有一萬種做法了。

我的做法:對於 \(\texttt{A}\) 區間沒有交的東西,判斷 \(\texttt{B}\) 區間有沒有交;對於 \(\texttt{B}\) 區間沒有交的東西,判斷 \(\texttt{A}\) 區間有沒有交。

下面就只說第一個了。

\(\texttt{A}\) 區間左端點從小到大排序。枚舉第一個東西,考慮 \(\texttt{A}\) 區間右端點小於這個枚舉的 \(\texttt{A}\) 區間的左端點的所有東西,都能作為第二個東西。

判斷 \(\texttt{B}\) 區間有沒有交,可以對每個第二個東西的 \(\texttt{B}\) 區間做區間加,然后判斷第一個東西的 \(\texttt{B}\) 的區間和是否為 \(0\)

時間復雜度 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=222222;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
struct seg{
	int la,ra,lb,rb;
}s1[maxn],s2[maxn];
int n,la[maxn],ra[maxn],lb[maxn],rb[maxn],tmpa[maxn],tla,tmpb[maxn],tlb;
ll b1[maxn],b2[maxn];
inline bool cmpla(const seg &x,const seg &y){return x.la<y.la;}
inline bool cmpra(const seg &x,const seg &y){return x.ra<y.ra;}
inline bool cmplb(const seg &x,const seg &y){return x.lb<y.lb;}
inline bool cmprb(const seg &x,const seg &y){return x.rb<y.rb;}
inline void update(ll *bit,int p,ll v){
	for(int i=p;i<=max(tla,tlb);i+=i&-i) bit[i]+=v;
}
inline ll query(ll *bit,int p){
	ll s=0;
	for(int i=p;i;i-=i&-i) s+=bit[i];
	return s;
}
inline void update(int p,ll v){
	update(b1,p,v);
	update(b2,p,1ll*p*v);
}
inline ll query(int p){
	return 1ll*(p+1)*query(b1,p)-query(b2,p);
}
inline void update(int l,int r,ll v){
	update(l,v);update(r+1,-v);
}
inline ll query(int l,int r){
	return query(r)-query(l-1);
}
int main(){
	n=read();
	FOR(i,1,n){
		la[i]=read();ra[i]=read();lb[i]=read();rb[i]=read();
		tmpa[++tla]=la[i];tmpa[++tla]=ra[i];
		tmpb[++tlb]=lb[i];tmpb[++tlb]=rb[i];
	}
	sort(tmpa+1,tmpa+tla+1);tla=unique(tmpa+1,tmpa+tla+1)-tmpa-1;
	sort(tmpb+1,tmpb+tlb+1);tlb=unique(tmpb+1,tmpb+tlb+1)-tmpb-1;
	FOR(i,1,n){
		la[i]=lower_bound(tmpa+1,tmpa+tla+1,la[i])-tmpa;
		ra[i]=lower_bound(tmpa+1,tmpa+tla+1,ra[i])-tmpa;
		lb[i]=lower_bound(tmpb+1,tmpb+tlb+1,lb[i])-tmpb;
		rb[i]=lower_bound(tmpb+1,tmpb+tlb+1,rb[i])-tmpb; 
		s1[i]=s2[i]=(seg){la[i],ra[i],lb[i],rb[i]};
	}
	sort(s1+1,s1+n+1,cmpla);
	sort(s2+1,s2+n+1,cmpra);
	int cur=1;
	FOR(i,1,n){
		while(cur<=n && s2[cur].ra<s1[i].la){
			update(s2[cur].lb,s2[cur].rb,1);
			cur++;
		}
		if(query(s1[i].lb,s1[i].rb)) return puts("NO"),0;
	}
	MEM(b1,0);MEM(b2,0);
	sort(s1+1,s1+n+1,cmplb);
	sort(s2+1,s2+n+1,cmprb);
	cur=1;
	FOR(i,1,n){
		while(cur<=n && s2[cur].rb<s1[i].lb){
			update(s2[cur].la,s2[cur].ra,1);
			cur++;
		}
		if(query(s1[i].la,s1[i].ra)) return puts("NO"),0;
	}
	puts("YES");
}

E

計算每個點 \(p\) 的貢獻。發現就是在剩下的點中選四個,能全在經過 \(p\) 的某一條直線的一側的就不滿足條件。

考慮從反面計算,用總方案數(在剩下的點中選四個),減去能全在經過 \(p\) 的某一條直線的一側的。

\(p\) 看成原點,剩下的點極角序排序。

枚舉最逆時針(應該懂什么意思)的點,剩下三個點應該能在一個區間中隨意選擇。計算這個區間是可以用雙指針的。

時間復雜度 \(O(n^2\log n)\)

注意,別用 atan2,最好用叉積判斷。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=5050;
const long double pi=3.141592653589793238;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int at(int x,int y){
	if(x>=0 && y>0) return 0;
	if(x<0 && y>=0) return 1;
	if(x<=0 && y<0) return 2;
	if(x>0 && y<=0) return 3;
	return -1;
}
struct point{
	int x,y;
	point(int xx=0,int yy=0):x(xx),y(yy){}
	point operator+(const point &p)const{return point(x+p.x,y+p.y);}
	point operator-(const point &p)const{return point(x-p.x,y-p.y);}
	point operator-()const{return point(-x,-y);}
	ll operator*(const point &p)const{return 1ll*x*p.y-1ll*y*p.x;}
	bool operator<(const point &p)const{
		if(at(x,y)!=at(p.x,p.y)) return at(x,y)<at(p.x,p.y);
		return *this*p>0;
	}
}p[maxn],q[maxn];
int n;
ll ans;
int main(){
	n=read();
	FOR(i,1,n) p[i].x=read(),p[i].y=read();
	FOR(i,1,n){
		FOR(j,1,n) if(j!=i) q[j-(j>i)]=p[j]-p[i];
		sort(q+1,q+n);
		FOR(j,1,n-1) q[j+n-1]=q[j];
		ans+=1ll*(n-1)*(n-2)*(n-3)*(n-4)/24;
		int cur=1;
		FOR(j,1,n-1){
			cur=max(cur,j);
			while(cur<2*n-2 && q[cur+1]*(-q[j])>0) cur++;
			int x=cur-j;
			ans-=1ll*x*(x-1)*(x-2)/6;
		}
	}
	printf("%lld\n",ans);
}

F/G

不會,咕了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM