第七次ACM訓練(Wednesday)

A題順序

13:00 比賽開始
13:23 a dxw
13:25 l yl
13:56 g dxw
14:40 k yl
17:13 i dxw
17:42 b yl

總結

第七次訓練成績還行,由於yl發的錯誤消息導致我們以爲不用訓練來着,zjl全程外出,只剩下我和yl孤軍奮戰,但從成績上來看還不錯……
收穫有2:1、簽到題就是因爲直接考的板子的要去掉ll之類的東西沒去掉導致本地跑的賊快交上去就wa了 2、對於複雜度較大的算法可以手動完成一些簡單特判迴避特殊數據加快時間 3、stl的multiset常數好大……

A - Mischievous Problem Setter (簽到)

description

n個點,每個點有Di和Ti,每個Di不同,現在按Di從小到大取,使時間加Ti,問在時間不超過m的情況下最多取多少個數

solution

不說了,快排一下

code

#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rp(i,a,b) for(int i=a;i>=b;i--)
using namespace std; 
const int maxn=2e5+5;
struct code{
	int a,b;
}a[maxn];
int n,m,i,t,j,k,l,x,y,z,mo,p,T,r,mid,q;
int ans,ans1;
bool cmp(code x,code y){
	return x.a<y.a || (x.a==y.a && x.b<y.b);
}
int main(){
	scanf("%d",&T);q=1;
	while (T--){
		scanf("%d%d",&n,&m);
		fo(i,1,n)scanf("%d",&a[i].a);
		fo(i,1,n)scanf("%d",&a[i].b);
		sort(a+1,a+n+1,cmp);ans=0;t=0;
		fo(i,1,n){
			t+=a[i].b;
		   	if(t<=m)ans++;
		   	else break;
		}
		printf("Case %d: %d\n",q++,ans);
	} 
}

B - Balance of the Force (2SAT+掃描線)

description

有n個騎士,每個騎士都能選擇加入黑暗或者光明的陣營,加入後的能力值分別爲D和L,已知有m對騎士不願意在同一個陣營,請問如何分配,能使得能力最高的騎士和能力最低的騎士之間的能力差值最小(n,m<=2e5,T<=20)

solution

我們先拆點i,i+n分別表示i染成黑色還是白色,若i,j不願在一個陣營,則i-j+n,j-i+n連邊表示選了i必選j+n,然後用一個並查集看看i和i+n是否在一個連通塊,若在則無解。否則我們可以處理出每個連通塊的最大值和最小值。然後到掃描線,將最大值從小到大排序,每次將新的集合加入set,若i+n已經存在,則考慮彈出最小值更小的那個即可

code

#include<bits/stdc++.h>
typedef long long LL; 
const int oo=0x3f3f3f3f; 
const int N=2e5+10; 
const int MOD=1e9+7;
using namespace std;
int read(){
	int f=1,s=0;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
	for(;c>='0'&&c<='9';c=getchar())s=s*10+c-'0';
	return f*s;
}
int fa[N+N];
struct qq{
	int id,min,max;
}a[N+N];
int find(int x){
	if(fa[x]!=x)fa[x]=find(fa[x]);
	return fa[x];
}
inline join(int x,int y){
	x=find(x);y=find(y);
	if(x<y)fa[y]=x;
	else if(x>y)fa[x]=y;
} 
bool cmp(qq a,qq b){
	return a.max<b.max;
}
vector<qq>v;
int n;
struct qqq{
	int id,n;
	qqq(int id_,int n_){
		id=id_;n=n_;
	}
};
bool operator<(qqq a,qqq b){
	return a.n>b.n; 
}
void work(){
	 n=read();int m=read();
	for(int i=1;i<=(n<<1);i++)fa[i]=i;
	while(m--){
		int x=read(),y=read();
		join(x,y+n);
		join(y,x+n);
	}
	for(int i=1;i<=n;i++)
		if(find(i)==find(i+n)){
			for(int i=1;i<=n;i++)read(),read();
			printf("IMPOSSIBLE\n");
			return;
		}
	for(int i=1;i<=(n<<1);i++){
		a[i].id=i;
		a[i].min=oo;
		a[i].max=0;	
	}
	for(int i=1;i<=n;i++){
		int l=read(),d=read();
		a[find(i)].min=min(a[find(i)].min,l);
		a[find(i)].max=max(a[find(i)].max,l);
		a[find(i+n)].min=min(a[find(i+n)].min,d);
		a[find(i+n)].max=max(a[find(i+n)].max,d);
	}
/*	for(int i=1;i<=n+n;i++)
		if(fa[i]==i)
			printf("%d %d %d\n",i,a[i].min,a[i].max);*/
	v.clear();
	for(int i=1;i<=n+n;i++)
		if(fa[i]==i)v.push_back(a[i]);
	sort(v.begin(),v.end(),cmp);
	map<int,int>f;
	priority_queue<qqq>q;
	int ans=oo;
	for(int i=0;i<v.size();i++){
		int x;
		if(v[i].id<=n)x=find(v[i].id+n);
		else x=find(v[i].id-n);
		if(f.find(x)==f.end()){
			f[v[i].id]=v[i].min;
			f.insert(pair<int,int>(v[i].id,v[i].min));
			q.push(qqq(v[i].id,v[i].min));
	//		printf("cishi:%d %d\n",v[i].id,v[i].min);
		}else{
			if(v[i].min>=f[x]){
				f.erase(x);
				f.insert(pair<int,int>(v[i].id,v[i].min));
				q.push(qqq(v[i].id,v[i].min));
			}
		}	 
		while(f.find(q.top().id)==f.end())q.pop();
	//	printf("%d %d %d\n",f.size(),v[i].max,q.top().n); 
		if(f.size()==(v.size()>>1))ans=min(ans,v[i].max-q.top().n);
	}
	printf("%d\n",ans);
} 


int main(){
//		freopen("in.txt","r",stdin);
//	freopen("my.txt","w",stdout);
	int T=read();
	for(int i=1;i<=T;i++){
		printf("Case %d: ",i);
		work();
	}
	return 0;
} 

D - Find Integer (費馬大定理)

description

給定a,n,找出an+bn=cna^n+b^n=c^n的一組解

solution

根據費馬大定理,n>2時無解……

code

#include<bits/stdc++.h>
using namespace std;
int read(){
	int f=1,s=0;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
	for(;c>='0'&&c<='9';c=getchar())s=s*10+c-48;
	return f*s;
} 
void work(){
	int n=read(),a=read();
	if(n>2){
		printf("-1 -1\n");
		return;
	}
	if(n==0){
		printf("-1 -1\n");
		return;
	}
	if(n==1){
		printf("1 %d\n",a+1);
		return;
	} 
	int b,c;
	if(a&1){
		c=(a*a+1)/2;
		b=c-1;
	}else{
		c=(a*a/2+2)/2;
		b=c-2;
	}
	printf("%d %d\n",b,c);

}
int main(){
	int T=read();
	while(T--)work();
}

G - Pastoral Life in Stardew Valley (組合數學)

description

給一個nm的矩形,在這個矩形中選擇一個矩形lr,在這個選擇的矩形裏再選擇一個矩形並不貼原矩形的邊界,問方案數。

solution

首先可以考慮長和寬分開考慮,最後答案相乘即可,對於長度爲x的邊,在它裏面選擇嚴格不貼邊的小邊的方案爲(nx+1)Cx12=nCx12(x1)3/2+(x1)2/2(n-x+1)C_{x-1}^2=n*C_{x-1}^2-(x-1)^3/2+(x-1)^2/2
x=1nnCx12(x1)3/2+(x1)2/2=nCn3mi3(n1)/2+mi2(n1)/2\sum_{x=1}^nn*C_{x-1}^2-(x-1)^3/2+(x-1)^2/2=n*C_n^3-mi3(n-1)/2+mi2(n-1)/2其中mi3(n),mi2(n)表示次方和和平方和

code

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define rp(i,a,b) for(ll i=a;i>=b;i--)
using namespace std;
const ll maxn=1e5+5;
const ll ni=166666668;
const ll ni2=500000004;
const ll ni4=250000002;
const ll mo=1e9+7;
ll c[maxn];
ll n,m,i,t,j,l,x,y,z,T,num,ans,q;
ll mi3(ll n){
	return n*(n+1)%mo*n%mo*(n+1)%mo*ni4%mo;
}
ll mi2(ll n){
	return n*(n+1)%mo*(2*n+1)%mo*ni%mo;
}
ll dg(ll n){
	return (n*c[n]%mo-mi3(n-1)*ni2%mo+mi2(n-1)*ni2%mo+mo)%mo;
}
int main(){
	//freopen("data.in","r",stdin);
	scanf("%lld",&T);
	fo(i,3,1e5)c[i]=i*(i-1)%mo*(i-2)%mo*ni%mo;q=0;
	while (T--){
		scanf("%lld%lld",&n,&m);
		ans=dg(n)*dg(m)%mo;
		printf("Case %lld: %lld\n",++q,ans);
	} 
}

I - Cockroaches (multiset)

description

n個點(xi,yi)(不存在重複),要求選擇一行一列使得被選中的點數量儘量多,並求出使得選中點數儘量多的不同方案數(不同方案指的是選中的點情況不同)(n<=1e5,T<=100)

solution

我們考慮兩種情況:1、橫豎交點有點 2、交點沒有點存在
記錄每一行每一列的的點的數量c[i],d[i],對於情況1,直接c[xi]+d[yi]-1,對於情況二,將所有d丟入multiset中,枚舉橫座標(座標壓縮後),把橫座標上的點的縱座標對應的d[y]取出後取最大值,然後再重新放回set中,複雜度O(NlogN)
不考慮答案爲2的情況極限數據14sTLE了,考慮完後2s,awsl……

code

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define rp(i,a,b) for(ll i=a;i>=b;i--)
using namespace std;
const int maxn=1e5+5;
int m,i,t,j,k,l,x,y,z,num,T,q,mx,mx1,ans;
ll n,coun;
struct code{
	int a,b;
}a[maxn];
int c[maxn],d[maxn];
multiset<int>f;
int read(){
	int f=1,s=0;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
	for(;c>='0'&&c<='9';c=getchar())s=s*10+c-'0';
	return f*s;
}
bool cmp(code x,code y){
	return x.a<y.a;
}
void make(){
	sort(a+1,a+n+1,cmp);
	int num=0,i=1,x=0;
	while (i<=n){
		if (a[i].a!=x) c[++num]=0;
		x=a[i].a;a[i].a=num;c[num]++;
		swap(a[i].a,a[i].b); i++;
	}
	fo(i,1,n) swap(c[i],d[i]);
}
int main(){
	//freopen("data.in","r",stdin);
	//freopen("data.out","w",stdout);
	T=read();q=0;
	while (T--){
		n=read();
		fo(i,1,n)a[i].a=read(),a[i].b=read();
		c[0]=d[0]=0;make();make();f.clear();
		ans=num=0;x=0;mx=mx1=0;
		fo(i,1,n){
			t=c[a[i].a]+d[a[i].b]-1;
			mx=max(mx,c[a[i].a]);
			mx1=max(mx1,d[a[i].b]);
			if (t>ans)ans=t,coun=1;
			else if (t==ans) coun++;
			if (a[i].b!=x) f.insert(d[a[i].b]);
			x=a[i].b;
		}
		if (max(mx,mx1)==1){
			if (n>=2) coun=n*(n-1)/2,ans=2;
			else coun=1,ans=1;
			printf("Case %d: %d %lld\n",++q,ans,coun);
			continue;
		}
		sort(a+1,a+n+1,cmp);i=1;
		while (i<=n){
				for(j=i;a[j].a==a[i].a && j<=n;j++)f.erase(f.find(d[a[j].b])); 
				if (!f.empty())x=*(--f.end());
				else x=0;
				t=c[a[i].a]+x;
				if (t>ans)ans=t,coun=f.count(x);
				else if (t==ans) coun+=f.count(x);
				for(j=i;a[j].a==a[i].a && j<=n;j++)f.insert(d[a[j].b]);
				i=j;
		}
		printf("Case %d: %d %lld\n",++q,ans,coun);
	} 
}

K-Mr. Panda and Kakin (exgcd+歐拉定理+快速乘)

description

c=f230+3modnc=f^{2^{30}+3} mod n給定n(n由兩個相鄰質數相乘p,q而得),c求f

solution

由歐拉定理ϕ(n)=(p1)(q1)\phi(n)=(p-1)(q-1) c=f(230+3)modϕ(n)modnc=f^{(2^{30}+3)mod\phi(n)} mod n
(230+3)modϕ(n)=q(2^{30}+3)mod\phi(n)=qf=cq1f=c^{q^{-1}},這裏q的逆元可以用exgcd求,也可以再用一次歐拉定理q1=qϕ(n)1modnq^{-1}=q^{\phi(n)-1} mod n由於隊友用exgcd,那我就講exgcd吧,ax+ny=gcd(a,n)=1
然後就是快速冪,這裏的乘法直接算會爆LL,所以要用到快速乘,快速乘原理是對於ab%c這種均在LL規模的計算,直接乘會爆LL,所以用d=(double)ab/c,然後ab%c==(ab-dc+c)%c,由於double保留前18位,d相當於ab/c\lfloor{a*b/c}\rfloor由於ab-d*c不會爆LL,所以只用讓它跳回正值就好了。

code

#include<bits/stdc++.h>
typedef long long LL; 
const int oo=0x3f3f3f3f; 
const int N=1e6+10; 
const int MOD=1e9+7;
using namespace std;
int read(){
	int f=1,s=0;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
	for(;c>='0'&&c<='9';c=getchar())s=s*10+c-'0';
	return f*s;
}
const int b=(1<<30)+3;
inline LL exgcd(LL a, LL b, LL& x, LL& y) {
    if(b==0){
    	x=1;y=0;
    	return a;
	}
	LL gcd=exgcd(b,a%b,y,x);
	y-=(a/b)*x;
	return gcd;
}
inline LL ni(LL a,LL p){
    LL x,y;
    exgcd(a,p,x,y);
    return (x%p+p)%p;
}

inline LL msc(LL a,LL b,LL c){
	return (a*b-(LL)((long double)a/c*b)*c+c)%c;
}
inline LL ksm(LL a,LL b,LL c){
	LL ans=1;
	for(;b;b>>=1,a=msc(a,a,c))
		if(b&1)ans=msc(ans,a,c);
	return ans;
}
void work(){
	LL n,c;
	scanf("%lld%lld",&n,&c);
	LL p=sqrt(n),q;
	while(n%p)p--;
	q=n/p;
	printf("%lld\n",ksm(c,ni(b,(p-1)*(q-1)),n));
} 


int main(){
	int T=read();
	for(int i=1;i<=T;i++){
		printf("Case %d: ",i);
		work();
	}
	return 0;
} 

L - Ultra Weak Goldbach’s Conjecture(exgcd+歐拉定理+快速乘)

description

給定一個n,問能否分解成6個質數的和

solution

若n爲奇數,則前4個位2,2,2,2,3,否則位2,2,2,2,那剩下的就爲哥德巴赫猜想內容

code

#include<bits/stdc++.h>
typedef long long LL; 
const int oo=0x3f3f3f3f; 
const int N=1e6+10; 
const int MOD=1e9+7;
using namespace std;
int read(){
	int f=1,s=0;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
	for(;c>='0'&&c<='9';c=getchar())s=s*10+c-'0';
	return f*s;
}
bool prime(LL n){
	if(n==1)return false;
	for(LL i=2;i*i<=n;i++)
		if(n%i==0)return false;
	return true;
}
void work(){
	LL n;
	scanf("%lld",&n);
	if(n<=11){
		printf("IMPOSSIBLE\n");
		return;
	}
	if(n&1)printf("2 2 2 3"),n-=9;
	else printf("2 2 2 2"),n-=8;
	LL nn=n+1;
	do{
		nn--;
		while(prime(nn)==false)nn--;
	}while(prime(n-nn)==false);
	printf(" %lld %lld\n",n-nn,nn);
} 


int main(){
	int T=read();
	for(int i=1;i<=T;i++){
		printf("Case %d: ",i);
		work();
	}
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章