第三次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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章