2019暑期第一阶段

有四题比较典型的dp放在另一篇

DAY1

Collision

将小球速度正交分解为x,y方向,x或y相同时答案是固定的,都不同时根据周期建立二元一次方程

\[2XA+\frac{2X-x1-x2}{2}=2YB+\frac{2Y-y1-y2}{2} \]

拓展gcd可解。
对于最小正整数解问题,由于当方程\(AX-BY=C\)有一组解(x0,y0)时,所有的解可表示为

\[\begin{cases}x=x0+kB\\y=y0+kA\end{cases} \]

于是在A,B>0且互质的条件下有xmin=(x0modB+B)modB,ymin=(y0modA+A)modA.
对于除以2的问题,预先都乘以2即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ggcd(ll x,ll y){
	if(y==0)return x;
	return ggcd(y,x%y);
}
ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0){
		x=1; y=0; return a;
	}
	ll r=exgcd(b,a%b,x,y);
	ll tmp=x;
	x=y;
	y=tmp-a/b*y;
	return r;
}
int main(){
	int t,cas=1;
	cin>>t;
	while(t--){
		ll x,y,x1,y1,x2,y2;
		double ans1,ans2;
		int f=1;
		scanf("%lld%lld%lld%lld%lld%lld",&x,&y,&x1,&y1,&x2,&y2);
		if(x1==x2&&y1==y2){
			ans1=x1; ans2=y1;
		}
		else if(x1==x2){
			double tmp=2*y-y1-y2;
			ans1=min(x1,x2)+tmp/2; ans2=tmp/2+min(y1,y2);
		}
		else if(y1==y2){
			double tmp=2*x-x1-x2;
			ans1=tmp/2+min(x1,x2); ans2=min(y1,y2)+tmp/2;
		}
		else{
			ll k1,k2;	
			x*=2; y*=2; x1*=2; y1*=2; x2*=2; y2*=2;
			ll c=(2*y-y1-y2-2*x+x1+x2)/2;
			ll gcd=ggcd(x,y);
			if(c%gcd)f=0;
			else{
				ll yy=y,xx=x;
			    y/=gcd; x/=gcd; c/=gcd;
			    exgcd(x,y,k1,k2);
			    k1*=c;
				k1=(k1%y+y)%y;
				k1=k1*xx+(2*xx-x1-x2)/2;
				ans1=(x1+k1)%(2*xx);
				ans2=(y1+k1)%(2*yy);
				if(ans1>xx)ans1=2*xx-ans1;
				if(ans2>yy)ans2=2*yy-ans2;
				ans1/=2; ans2/=2;
			}
		}
		printf("Case #%d:\n",cas++);
		if(f){
			printf("%.1lf %.1lf",ans1,ans2);
		}
		else{
			printf("Collision will not happen.");
		}
		printf("\n");
	}
	return 0;
}

Happy Matt Friends

用所有可能的组合数\(2^n\)减去能生成小于m-1的组合数即可。
一个很显然的结论:n个数线性基意义下的秩为p时,如果一个数可由这些数异或得到,那么得到它的方法一定有\(2^{n-p}\)种。

#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
struct  LinearBase {
    static const int maxl = 63;
    ll a[maxl + 5];
    int cnt;
    LinearBase() { cnt=0; memset(a,0,sizeof(a)); }
    void clear() { cnt=0; memset(a,0,sizeof(a)); }
    void insert(ll x) {
        for (int i = maxl - 1; i >= 0; i--) {
            if (x & (1ll << i)) {
                if (a[i]) x ^= a[i];
                else {
                    for (int k = 0; k < i; k++) 
                        if (x & (1ll << k)) x ^= a[k];
                    for (int k = i + 1; k < maxl; k++) 
                        if (a[k] & (1ll << i)) a[k] ^= x;
                    a[i] = x; cnt++;
                    return ;
                }
            }
        }
    }
    bool check(ll x) {
        for (int i = maxl - 1; i >= 0; i--) {
            if (x >> i & 1) {
                if (a[i]) x ^= a[i];
                else return false;
            }
        }
        return true;
    }
    ll qmax(int x) {
        ll res = x;
        for(int i = maxl - 1 ; i >= 0; i--) {
            if ((res ^ a[i]) > res) res ^= a[i];
        }
        return res;
    }
}p;
int main(){
	int cas=1,t; cin>>t;
	while(t--){
		ll n,m,a[50];
		scanf("%lld%lld",&n,&m);
		p.clear();
		for(int j=1;j<=n;j++){
			scanf("%lld",a+j);
			p.insert(a[j]);
		}
		ll ans=0;
		for(ll i=0;i<m;i++)if(p.check(i))ans++;
		ans*=1ll<<(n-p.cnt);
		ans=(1ll<<n)-ans;
		printf("Case #%d: %lld\n",cas,ans); cas++;
	}
}

Intersection

类似于容斥的思想:大大相交面积-大小相交面积+小小相交面积。

DAY2

Signal Interference

计算几何模板题,阿氏圆+圆与多边形相交面积。

DAY3

Problem Buyer

仅仅考虑区间覆盖的话只需要取一个max(n-xi+1)即可,xi是第i个点有多少个区间可以覆盖它。但是这一题还要为每个点至少分配一个区间,所以从左到右求xi时每次应该删去一个对后面影响最小的区间,可以用一个对区间右端点的小顶优先队列维护。

#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> > p;
int n,m,q[100005];
struct  node{
	int l,r;
}a[100005];
bool cmp(node x,node y){
	return x.l==y.l?x.r>y.r:x.l<y.l;
}
int main(){
	int t,cas=1;
	cin>>t;
	while(t--){
		while(!p.empty())p.pop();
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)scanf("%d%d",&a[i].l,&a[i].r);
		for(int i=1;i<=m;i++)scanf("%d",q+i);
		sort(a+1,a+n+1,cmp);
		sort(q+1,q+m+1);
		int j=1;
		int ans=0;
		for(int i=1;i<=m;i++){
			while(j<=n&&a[j].l<=q[i]){
				p.push(a[j].r); j++;
			}
			while(!p.empty()&&p.top()<q[i])p.pop();
			ans=max(ans,n-int(p.size())+1);
			if(!p.empty())p.pop();
		}
		if(ans>n){
			printf("Case #%d: IMPOSSIBLE!\n",cas); cas++;
		}
		else{
			printf("Case #%d: %d\n",cas,ans); cas++;
		}
	}
}

Pandaland

每次去掉一个边找相连顶点的最短路,加上边的权值即是一个环的权值,最后取一个min。难在建图,可以用二维数组code[][]表示这个点的编号。跑dijk的过程可以加一个剪枝if(dis[u]-w>=ans)return;

DAY4

Subway Chasing

传说中的差分约束,到现在都没写对,mark一下明天再写。

DAY5

Land of Farms

\(2^{10}\)枚举ancient farm的状态,去掉周围直接相连的点,对剩下的二分图匹配找最大独立点集。
(还没写,mark一下明天写)

The Shields

三维偏序问题,套用CDQ分治。
(没写对,mark一下明天写)

DAY6

Instability

Ramsey定理:六个人中必有三人两两相识或两两不相识。
直接枚举3,4,5个点成立的情况,再加上\(\sum_{i=6}^{n}C_{n}^{i}\)即可。

DAY7

TOO RICH

dfs反向贪心,找组成sum-p的最小硬币数。注意硬币按面值从大到小扫过渡到下一层时有两种状态:大硬币取now/w个或者取now/w-1个,这里面其实有一种取模怎样更划算的问题。

#include<bits/stdc++.h>
using namespace std;
struct  node{
	int w,s;
}a[15];
int p,ans;
void dfs(int res,int k,int la){
	if(res==0){
		ans=min(ans,k); return;
	}
	int sum=0;
	for(int i=1;i<=10;i++)if(a[i].w<=res)sum+=a[i].s*a[i].w;
	if(sum<res)return;
	if(k>=ans)return;
	for(int i=la-1;i>=1;i--){
		if(a[i].s==0||a[i].w>res){
			continue;
		}
		int tmp=min(res/a[i].w,a[i].s);
		a[i].s-=tmp;
		dfs(res-tmp*a[i].w,k+tmp,i);
		a[i].s+=1;
		dfs(res-(tmp-1)*a[i].w,k+tmp-1,i);
		a[i].s+=tmp-1;
	}
}
const int ch[]={1,5,10,20,50,100,200,500,1000,2000};
int main(){
	int t; cin>>t;
	while(t--){
		scanf("%d",&p);
		int sum=0,sum1=0;
		for(int i=1;i<=10;i++)scanf("%d",&a[i].s),a[i].w=ch[i-1],sum+=a[i].w*a[i].s,sum1+=a[i].s;
		ans=0x3f3f3f3f;
		dfs(sum-p,0,11);
		if(ans==0x3f3f3f3f)printf("%d\n",-1);
		else printf("%d\n",sum1-ans);
	}
}

Rebuild

毒瘤卡精度题。点为奇数时半径是固定的,求出来判断一下就好。为偶数时先求r1的范围,再用一元二次方程判断r1取值。注意有常数pi的时候尽量提出来。

Partial Tree

一个结论:任意给出图上n个点的度,保证这些度数和为2n-2,总能用这些点构造出一棵树。(具体怎么构造呢。。。还没想通)
于是这题就变成了背包裸题。

DAY8

Recursive sequence

递推式:\(a_{n}=a_{n-1}+2a_{n-2}+n^4\)
构建下面这个神奇的矩阵乘法(我也不知道要怎么去想,但是瓜神就是想出来了,orz):

\[\begin{bmatrix} 1&2&0&1&0&0&0&0\\ 1&0&0&0&0&0&0&0\\ 0&1&0&0&0&0&0&0\\ 0&0&0&1&4&6&4&1\\ 0&0&0&0&1&3&3&1\\ 0&0&0&0&0&1&2&1\\ 0&0&0&0&0&0&1&1\\ 0&0&0&0&0&0&0&1\\ \end{bmatrix}^{n-3} \begin{bmatrix} a_3\\ a_2\\ a_1\\ i^4\\ i^3\\ i^2\\ i\\ 1 \end{bmatrix}\]

再套用矩阵快速幂。

Do not pour out

这题并没有用真算法通过。。。求体积实际上要用很复杂的积分。。。然而高数功底太弱(哭死)
找时间重新做一下这个数学题。

DAY9

Frogs

很奇妙的数列模拟容斥过程,对照代码可以理解。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m,a[100005];
ll gt(ll t){
	ll k=__gcd(t,m);
	ll tmp=k;
	k=m/k;
	ll ans=(m-tmp)*k/2;
	return ans;
}
ll b[100005],cnt=0;
int main(){
	ll t,cas=1;
	cin>>t;
	while(t--){
		cnt=0;
		scanf("%lld%lld",&n,&m);
		for(ll i=1;i<=n;i++)scanf("%lld",a+i);
		for(ll i=1;i<=n;i++)b[++cnt]=__gcd(a[i],m);
		sort(b+1,b+cnt+1);
		ll ss=unique(b+1,b+cnt+1)-b-1;
		ll num2[10005]={0};
		cnt=0;
		for(ll i=1;i*i<=m;i++){
			if(m%i==0){
				num2[++cnt]=i; if(i!=1&&i*i!=m)num2[++cnt]=m/i;
			}
		}
		sort(num2+1,num2+cnt+1);
		ll p1=1,p2=1;
		ll ans1[10005]={0},ans2[10005]={0};
		while(p1<=ss){
			while(p2<=cnt&&num2[p2]!=b[p1])p2++;
			ans1[p2]=1;
			for(int i=p2;i<=cnt;i++)if(num2[i]%num2[p2]==0)ans1[i]=1;
			p1++; p2++;
		}
		
		for(ll i=1;i<cnt;i++){
			if(ans1[i]-ans2[i]==0)continue;
			ll tmp=ans1[i]-ans2[i];
			for(ll j=i+1;j<=cnt;j++)if(num2[j]%num2[i]==0)ans2[j]+=tmp;
		}
		for(ll i=1;i<=cnt;i++)ans1[i]=ans1[i]-ans2[i];
		ll s=0;
		for(ll i=1;i<=cnt;i++)s+=gt(num2[i])*ans1[i];
		printf("Case #%lld: %lld\n",cas++,s);
	}
	return 0;
}

DAY10

Bomb

缩点?塔尖?居然什么都不知道(哭死)mark一下明天学。

Kindom of obsession

2e9内质数间隔不会超过三百,对n>300输出NO,对n小于300用二分图匹配。

END

markdown是个好东东。。。

posted @ 2019-07-11 22:13  rain_star  阅读(197)  评论(1编辑  收藏  举报