第七次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
#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
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,找出 a n + b n = c n a^n+b^n=c^n an+bn=cn的一组解

solution

根据费马大定理,n>2时无解……

code

#include
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的边,在它里面选择严格不贴边的小边的方案为 ( n − x + 1 ) C x − 1 2 = n ∗ C x − 1 2 − ( x − 1 ) 3 / 2 + ( x − 1 ) 2 / 2 (n-x+1)C_{x-1}^2=n*C_{x-1}^2-(x-1)^3/2+(x-1)^2/2 (nx+1)Cx12=nCx12(x1)3/2+(x1)2/2
∑ x = 1 n n ∗ C x − 1 2 − ( x − 1 ) 3 / 2 + ( x − 1 ) 2 / 2 = n ∗ C n 3 − m i 3 ( n − 1 ) / 2 + m i 2 ( n − 1 ) / 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 x=1nnCx12(x1)3/2+(x1)2/2=nCn3mi3(n1)/2+mi2(n1)/2其中mi3(n),mi2(n)表示次方和和平方和

code

#include
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a
#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
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a
#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 = f 2 30 + 3 m o d n c=f^{2^{30}+3} mod n c=f230+3modn给定n(n由两个相邻质数相乘p,q而得),c求f

solution

由欧拉定理 ϕ ( n ) = ( p − 1 ) ( q − 1 ) \phi(n)=(p-1)(q-1) ϕ(n)=(p1)(q1) c = f ( 2 30 + 3 ) m o d ϕ ( n ) m o d n c=f^{(2^{30}+3)mod\phi(n)} mod n c=f(230+3)modϕ(n)modn
( 2 30 + 3 ) m o d ϕ ( n ) = q (2^{30}+3)mod\phi(n)=q (230+3)modϕ(n)=q f = c q − 1 f=c^{q^{-1}} f=cq1,这里q的逆元可以用exgcd求,也可以再用一次欧拉定理 q − 1 = q ϕ ( n ) − 1 m o d n q^{-1}=q^{\phi(n)-1} mod n q1=qϕ(n)1modn由于队友用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相当于 ⌊ a ∗ b / c ⌋ \lfloor{a*b/c}\rfloor ab/c由于ab-d*c不会爆LL,所以只用让它跳回正值就好了。

code

#include
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
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;
} 

你可能感兴趣的:(icpc)