第三次icpc集训(Wednesday)

总结

第三次训练感觉我的状态比上一次又好了一些,但还不够熟练,主要是小错误不断犯,不能一次性打出bug较少的代码。

A - Ascending Rating (单调队列+路径压缩)

description

给定长度为n(<=1e7)的序列,每次从点l开始,每次在x点上,选择在[l,l+m-1]比当前点x值更大的点并跳过去,设maxrating为最后站立的点的值,count为经历的点的个数,统计从每个点开始的maxrating和count
在这里插入图片描述
输出A,B,有多组数据
在这里插入图片描述

solution

我们先用单调队列找出每个点i往后第一个比他大的数的位置fa[i],然后从前往后枚举x,每次像并查集一样往前搜索并压缩路径,但只回溯的范围不超过x+m-1,压缩路径并更新路径上的点的答案即可。
最狗的是这里的n在1e7的范围,直接像并查集递归找可能会爆栈(???,我tm第一次会并查集爆栈!!)

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=1e7+5;
ll a[maxn],d[maxn],f[maxn],fa[maxn];
ll n,m,i,t,j,k,l,x,y,z,mo,p,T,r,mid,q;
ll ans,ans1;
ll read(){
	char ch=getchar();ll x=0;
	while (ch<48 || ch>57) ch=getchar();
	while (ch>=48 && ch<=57) x=x*10+ch-48,ch=getchar();
	return x;
}
int getfa(int x,int y){
	d[0]=1;d[1]=x;
	for(;fa[x]-y<=m-1;x=fa[x])d[++d[0]]=fa[x];
	rp(i,d[0]-1,1){ 
		if (d[i+1]!=x)f[d[i]]+=f[d[i+1]];
		fa[d[i]]=x; 
	} 
	return x;
}
int main(){
	scanf("%lld",&T);
	while (T--){
		scanf("%lld%lld%lld%lld%lld%lld%lld",&n,&m,&k,&p,&q,&r,&mo);
		fo(i,1,k)a[i]=read();
		fo(i,k+1,n)a[i]=(p*a[i-1]+q*i+r)%mo;
		d[0]=0;
		rp(i,n,1){
			while (d[0] && a[d[d[0]]]<=a[i]) d[0]--;
			if (d[0]) fa[i]=d[d[0]];
			else fa[i]=n+1;
			d[++d[0]]=i;f[i]=1;
		}
		ans=ans1=0;
		fo(i,1,n-m+1){
			x=getfa(i,i);
			ans+=(a[x]^i);
			if (x==i) t=f[i];
			else t=f[i]+f[x];
			ans1+=(t^i);
		}
		printf("%lld %lld\n",ans,ans1);
	}
}

C Dynamic Graph Matching (状压dp)

description

给定n(<=10)个点,初始互不连通,有m(<3e4)个操作,每次操作可以加入或删除已存在的一条边,要求每次操作后输出选择k(0…n/2)条端点没有交集的边的方案。

solution

设f[s]表示状态集为S,若为加入,则枚举含u,v的状态S,f[s]+=f[s-(1<<u)-(1<<v)],若为减则同理。时间复杂度O(m*2^n)

code

#include<bits/stdc++.h>
using namespace std;
const int mo=1e9+7;
int main(){
	char s[10];
	long long t,n,m,dp[1<<11],ans[20],x,y,counts[1<<11];
	cin>>t;
	for(int i=1;i<1<<11;i++) counts[i]=__builtin_popcount(i);
	while(t--){
		cin>>n>>m;
		memset(dp,0,sizeof(dp));
		dp[0]=1;
		while(m--){
			memset(ans,0,sizeof(ans));
			scanf("%s %d %d",s,&x,&y);
			x--;
			y--;
			if(s[0]=='+'){
				for(int i=0;i<=(1<<n)-1;i++)
					if((i&(1<<x))&&(i&(1<<y)))
						dp[i]=(dp[i]+dp[i-(1<<x)-(1<<y)]+mo)%mo;					
			}
			else{
				for(int i=0;i<=(1<<n)-1;i++)
				   if((i&(1<<x))&&(i&(1<<y)))
				        dp[i]=(dp[i]-dp[i-(1<<x)-(1<<y)]+mo)%mo;
			}
			for(int i=0;i<=(1<<n)-1;i++) ans[counts[i]]=(ans[counts[i]]+dp[i]+mo)%mo;
			for(int i=1;i<=n/2;i++){
				cout<<ans[i*2];
				if(i!=n/2) cout<<" ";
			}
			cout<<endl;
		}
	}
	return 0;
} 

D Dynamic Graph Matching (结论题?)

description

求第k个欧拉函数为合数的数

solution

同队的大佬打表出结论……
我们知道欧拉函数是积性函数,若a,b互质,φ(ab)=φ(a)φ*(b),在线性筛中,b永远是质数,所以若a,b不互质,那么φ(ab)=φ(a)*b,这说明了什么?除了开始的几个φ(a)有可能是质数外,其他必定是合数!!!
然后,没然后了。

code

#include<bits/stdc++.h>
using namespace std;
int main(){
	long long t,k;
	cin>>t;
	while(t--){
		cin>>k;
		k=k+5;
		if(k==6) k--;
		cout<<k<<endl;
	}
	return 0;
} 

F Grab The Tree (博弈)

description

一棵树,每个节点上有权值,现在在树上任意取一些点,要求这些点在树上不能相邻,要求这些点最优条件下的xor和是否比剩下的点的xor和大。

solution

这题好险不是我打,否则我就往树形dp那边去想了……

我们发现取下来的点的异或和xor上剩下的点的异或和为所有点的异或和,若所有点的异或和不为0,那么我们只要取最高位为1的随便一个点即可,若为0肯定平局。

code

#include<iostream>
#include<cstdio>
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 main(){
	int T=read();
	while(T--){
		int sum=0;
		int n=read();
		
		for(int i=0;i<n;i++)
			sum^=read();
		for(int i=1;i<n;i++)read(),read();
		if(sum==0)printf("D\n");
		else printf("Q\n");
		
		
	}
	return 0;
}

G - Interstellar Travel (计算几何)

description

有n个点(x,y),从i到j的(xi<xj)的代价为xi* yj-xj* yi,(保证x1<x2,x3,……<xn,y1=yn=0,0<=xi,yi<=1e9),要求一条从1到n的路径,使得代价最小

solution

我们假设现在有(x1,y1),(x2,y2),(x3,y3)(x1<x2<x3)3个点,我们计算什么时候从1直接跳到3比1到2再到3更优
从1到3:x1y3-x3y1
从1到2到3:x1y2-x2y1+x2y3-x3y2
下减上得:x1y2-x2y1+x2y3-x3y2-x1y3+x3y1>0
化简得:x1(y2-y3)+x3(y1-y2)-x2(y1-y2+y2-y3)>0
(y2-y3)/(x2-x3)>(y1-y2)/(x1-x2)
k32>k21k_{32}>k_{21}
反之,若k32<k21,那么2就成了必须点,仔细想想,这不就是上凸壳吗?所以按x轴排序后求出了一个上凸壳即可

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=2e5+5;
ll n,m,i,t,j,k,l,x,y,z,T;
ll d[maxn],p[maxn];
struct code{
	ll x,y,id;
}a[maxn];
bool cmp(code x,code y){
	return x.x<y.x || x.x==y.x && x.y>y.y || x.x==y.x && x.y==y.y && x.id<y.id;
}
bool pan(ll x,ll y,ll z){
	if (a[y].x==a[z].x) return 0;
	ll p=(a[y].y-a[x].y)*(a[z].x-a[y].x),
	q=(a[z].y-a[y].y)*(a[y].x-a[x].x);
	if (p<q) return 1;
	else if (p>q) return 0;
	return a[y].id>a[z].id;
} 
int main(){
	scanf("%lld",&T);
	while (T--){
		scanf("%lld",&n);
		fo(i,1,n)scanf("%lld%lld",&a[i].x,&a[i].y),a[i].id=i;
		sort(a+1,a+n+1,cmp);t=2;
		fo(j,3,n)
			if (pan(1,t,j)) t=j;
		d[1]=1;d[0]=2;d[2]=t;
		fo(j,t+1,n){
			while (pan(d[d[0]-1],d[d[0]],j) && d[0]>2) d[0]--;
			if (a[j].x!=a[d[d[0]]].x)d[++d[0]]=j;
		}
		fo(i,1,d[0]-1) printf("%lld ",a[d[i]].id);
		printf("%lld\n",a[d[d[0]]].id);
	}
}

L - Visual Cube (模拟)

description

给你长方体得长宽高,打印出长方体得立体图形

solution

别问我怎么打出来的,我也不知道他怎么打出来的……

code

#include<iostream>
#include<cstdio>
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;
}
char t[110][110];
void dodo(int x,int y,int a,int c){
	int xx=x,yy=y;
	t[x][y]='+';t[x+1][y-1]='/';
	while(a--){
		y--;
		t[xx][y]='-';
		y--;
		t[xx][y]='+';
		t[xx+1][y-1]='/';
	}
	while(c--){
		x++;
		t[x][yy]='|';
		x++;
		t[x][yy]='+';
		t[x+1][yy-1]='/';
	}
}
void work(int a,int b,int c){
	int m=b+b+c+c+1;
	int n=a+a+b+b+1;
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
			t[i][j]='.';
	for(int i=b+b+1;i<=m;i++,i++){
		for(int j=1;j<=a+1;j++)
			t[i][j+j-1]='+',t[i][j+j]='-';
		t[i][a+1+a+1]='.';
		for(int j=1;j<=a+1;j++)
			t[i+1][j+j-1]='|';
	}
	for(int i=1;i<=b;i++)
		dodo(i+i-1,n+2-i-i,a,c);	
			
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++)
			putchar(t[i][j]);
		putchar('\n');
	}
}
int main(){
	int T=read();
	while(T--){
		int a=read(),b=read(),c=read();
		work(a,b,c);		
	}
	return 0;
}

M - Walking Plan (分块+floyd)

description

一张n(<=50)个点m(<=10000)条边(无自环)得有向图,q(<=1e5)个询问,每次询问从s到t经过至少k条边的最小花费

solution

事实证明,分块比二分更为优秀awsl

我们按100分块,设f[k][i][j]表示从i到j恰好走100*k条边的最小费用

d[k][i][j]表示从i到j恰好走k条边的最少费用

但是题目说的是至少走k条边,因此我们需要对d数组进行一些改造

手动的让d多跑一些边(比如跑120条边)

然后从120到1倒着取min

(为什么是120这个数字?随缘取的,原则上更大一些更安全?

正确性?(或许可以证明吧、然而不太会

答案就是min(f[k/100][s][j]+f[x%100][j][t])

(通过j点进行中转)

复杂度O(T(qn+100n3)),复杂度O(T(qn+100n3))

code

#include<bits/stdc++.h>
typedef long long LL; 
const int oo=0x3f3f3f3f; 
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 N=60;
int f[150][N][N],d[160][N][N],n,m;

void work(){
	n=read(),m=read();
	memset(f,0x3f,sizeof(f));
	memset(d,0x3f,sizeof(d));

	for(int i=1;i<=n;i++)
		f[0][i][i]=d[0][i][i]=0;

	for(int i=0;i<m;i++){
		int x=read(),y=read();
		d[1][x][y]=min(d[1][x][y],read());
	}

	for(int t=2;t<=120;t++)
		for(int k=1;k<=n;k++)
			for(int i=1;i<=n;i++)
				for(int j=1;j<=n;j++)
					d[t][i][j]=min(d[t][i][j],d[t-1][i][k]+d[1][k][j]);	
	for(int t=119;t>=0;t--)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				d[t][i][j]=min(d[t][i][j],d[t+1][i][j]);

	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)	
			f[1][i][j]=d[100][i][j];			
	for(int t=2;t<=100;t++)
		for(int k=1;k<=n;k++)
			for(int i=1;i<=n;i++)
				for(int j=1;j<=n;j++)
					f[t][i][j]=min(f[t][i][j],f[t-1][i][k]+f[1][k][j]);

	int q=read();
	for(int i=0;i<q;i++){
		int s=read(),t=read(),k=read();
		int ans=oo;

		for(int j=1;j<=n;j++)
			ans=min(ans,f[k/100][s][j]+d[k%100][j][t]);

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