1. 程式人生 > >20181214第一週周訓思路整理

20181214第一週周訓思路整理

第一週題解

訓練時間:2018/12/14-2018/12/21 author:wlxsq

Problem List

Problem Answer
  • BZOJ4300:絕世好題 連結
/*
	BZOJ4300:絕世好題 
	一道很不錯的動歸題
	普通想法dp[i]表示以i作為結束的最長長度,類似於最長上升子序列O(n^2)的思路。
	很顯然,這種做法是會TLE的。
	但是這題有不能夠像LIS那樣有O(nlogn)的時間複雜度做法,因為他要做與運算,會有後效性。 
	正解:
	dp	1 2 3 ... 31	表示對應的二進位制位的最長長度
		0 0 0 ... 0
	對於每一個b[i],其b[i-1]二進位制位只要有一位不為0,則b[i]&b[i-1]!=0
	所以只要找到b[i]二進位制位出現的最長長度,更新就好。 
*/ 
#include<iostream>
#include<algorithm>
#include<cstdio> 
#include<cstring>
#include<string> 
using namespace std;
const int N = 100005;
int a[N];
int dp[100];	//	dp[i]表示二進位制位i位置上的最長長度。 
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+(ch-'0');ch=getchar();}
	return f*x;
}
int main()
{
	int n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++)
	{
		int tmp=0; 
		for(int j=0;j<=30;j++)	//	找出與運算之後不為0的最長長度 
		{
			if(a[i]&(1<<j)) tmp=max(tmp,dp[j]+1);
		}
		for(int j=0;j<=30;j++)	//	更新最長長度 
		{
			if(a[i]&(1<<j)) dp[j]=tmp;
		}
	} 
	int ans=0;
	for(int i=0;i<=30;i++) ans=max(ans,dp[i]);
	cout<<ans<<endl;
	return 0;
}

完成時間:2018/12/12 14:12

  • Bailian1308: Is It A Tree? 連結
/*
	給出一副圖,判斷是否能夠形成一棵樹。
	並查集處理即可。 
	1、存在環,
	2、只有一個根節點(點的個數=邊的個數+1) 
	3、直接輸入0 0表示一顆空樹,is a tree
	4、題目沒有給出資料範圍,所有隻能儘可能的開大一點 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map> 
using namespace std;
const int N = 1000005;
int f[N],Case=1,a,b,flag,cnt_v,cnt_e;
map<int,int>Map;
int Find(int x)
{
	while(f[x]!=x) return f[x]=Find(f[x]);
	return f[x];
}
void init()
{
	flag=1,cnt_v=cnt_e=0;
	for(int i=0;i<N;i++) f[i]=i;
	Map.clear();
}
int main()
{
	init();
	do{
		cin>>a>>b;
		if(a||b)
		{
			if(Map[a]==0) cnt_v++;
			if(Map[b]==0) cnt_v++;
			cnt_e++;Map[a]++,Map[b]++;
		}
		if(a==-1&&b==-1) break;
		else if(a==0&&b==0)
		{
			if(cnt_v&&(cnt_v!=cnt_e+1))flag=0;
			if(flag) printf("Case %d is a tree.\n",Case++);
			else printf("Case %d is not a tree.\n",Case++);
			init();
		}else 
		{
			int x=Find(a),y=Find(b);
			if(x==y) flag=0;
			if(flag) f[x]=y;
		}
	}while(1);
	return 0;
}

完成時間:2018/12/12 15:40

  • HDU2594: Simpsons’ Hidden Talents連結
/*
	給出兩個字串S1和S2,求S1的字首和S2的字尾最長公共長度。
	s1和s2的長度為len:50000。
	暴力做法時間複雜度:O(len^2)
	深入理解KMP的next陣列。
	求next陣列時間複雜度:O(len) 
	next[i]表示子串[0,i-1]的前後綴匹配的最長長度 
	注意:
		1、aaa aaa 
*/ 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000005;
int nxt[N];
string s1,s2;
int min(int a,int b)
{
	return a>b?b:a;
}
void get_next(string s)
{
	int j=0,k=-1,len=s.size();
	nxt[0]=-1;
	while(j<len)
	{
		if(k==-1||s[j]==s[k])
		{
			nxt[++j]=++k;
		}
		else k=nxt[k];
	}
	nxt[len]=min(nxt[len],min(s1.size(),s2.size()));
	for(int i=0;i<nxt[len];i++) cout<<s[i];
	if(nxt[len]) cout<<' ';
	cout<<nxt[len]<<endl;
}
int main()
{
	while(cin>>s1>>s2) get_next(s1+s2);
	return 0;
} 

完成時間:2018/12/12 16:35

  • POJ1511:Invitation Cards 連結
#pragma GCC optimize(2)
/*
	這個題目還是比較簡單的,思考難度不大
	求從一個點出發,到達其餘所有的點路徑之和
	再求從其餘所有點回到出發點的路徑之和
	注意:
		1、N=M=1000000
			O(n^2)的時間複雜度肯定是過不了的,得O(nlogn)
		全部都是正數,堆+dijkstra完美解決問題。
		2、常數太大。。。vector<>死活TLE... 故手寫鄰接表
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const long long N = 1000015;
long long dist1[N],dist2[N];
typedef pair<long long,int>P;
vector<P>G1[N],G2[N];
int t,n,m,u,v,w;
const long long INF= 1e15;
inline long long read()
{
	long long x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+(ch-'0');ch=getchar();}
	return f*x;
}
struct Edge
{
	int nxt,to;
	long long w;
}E1[N],E2[N];
int cnt1=0,cnt2=0,head1[N],head2[N];
void add1(int u,int v,long long w)
{
	E1[++cnt1].nxt=head1[u];
	E1[cnt1].to=v;
	E1[cnt1].w=w;
	head1[u]=cnt1;
}
void add2(int u,int v,long long w)
{
	E2[++cnt2].nxt=head2[u];
	E2[cnt2].to=v;
	E2[cnt2].w=w;
	head2[u]=cnt2;
}
struct cmp
{
	bool operator()(const P p1,const P p2)
	{
		return p1.first > p2.first;
	}
};
priority_queue<P,vector<P>,greater<P> >q;
inline long long dij1()
{
	for(int i=0;i<=n;i++) dist1[i]=INF;dist1[1]=0;
	while(!q.empty()) q.pop();
	q.push(P(0,1));
	while(!q.empty())
	{
		P tt=q.top();q.pop();
		u=tt.second;
		if(tt.first!=dist1[u]) continue;
		for(register int i=head1[u];i;i=E1[i].nxt)
		{
			int v=E1[i].to;long long w=E1[i].w;
			if(dist1[v]>dist1[u]+w)
			{
				dist1[v]=dist1[u]+w;
				q.push(P(dist1[v],v));
			}
		}
	}
	long long res=0;
	for(register int i=1;i<=n;i++) res+=dist1[i],G1[i].clear();
	return res;
}
inline long long dij2()
{
	for(int i=0;i<=n;i++) dist2[i]=INF;dist2[1]=0;
	while(!q.empty()) q.pop();
	q.push(P(0,1));
	while(!q.empty())
	{
		P tt=q.top();q.pop();
		u=tt.second;
		if(tt.first!=dist2[u]) continue;
		for(register int i=head2[u];i!=0;i=E2[i].nxt)
		{
			int v=E2[i].to;long long w=E2[i].w;
			if(dist2[v]>dist2[u]+w)
			{
				dist2[v]=dist2[u]+w;
				q.push(P(dist2[v],v));
			}
		}
	}
	long long res=0;
	for(register int i=1;i<=n;i++) res+=dist2[i],G2[i].clear();
	return res;
}
int main()
{
//	freopen("invite.in","r",stdin);
//	scanf("%d",&t);
	t=read();
	while(t--)
	{
		n=read();m=read();
		cnt1=cnt2=0;
		memset(head1,0,sizeof(head1));
		memset(head2,0,sizeof(head2));
//		scanf("%d%d",&n,&m);
		for(register int i=0;i<m;i++)
		{
			u=read();v=read();w=read();
//			scanf("%d%d%d",&u,&v,&w);
//			G1[u].push_back(P(v,w));
//			G2[v].push_back(P(u,w));
			add1(u,v,w);
			add2(v,u,w);
		}
		long long ans=dij1()+dij2();
		printf("%lld\n",ans);
	}
	return 0;
}

完成時間:2018/12/13 13:21

/*
	最小生成樹題
	Kruskal	時間複雜度:O(ElogE)
	Prim	時間複雜度:O(n^2) O(nlogn)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define INF 1e9
using namespace std;
const int N = 1005;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=x*10+(ch-'0');ch=getchar();}
	return f*x;
}
struct node
{
	int x,y;
}V[N];
int Map[N][N];
int dist[N];
int vis[N];
int pre[N];
int n,m,x,y;
double MST;
void prim()
{
	for(int i=1;i<=n;i++) pre[i]=1,dist[i]=Map[1][i];dist[1]=0;
	vis[1]=1;
	for(int i=2;i<=n;i++)
	{
		int v=-1;
		for(int j=1;j<=n;j++)
			if(!vis[j]&&(v==-1||dist[v]>dist[j])) v=j;
		if(v==-1) break;
		if(Map[pre[v]][v]!=0)cout<<pre[v]<<' '<<v<<endl;
		vis[v]=1;
		for(int j=1;j<=n;j++)
		{
			if(dist[j]>Map[v][j])
			{
				dist[j]=Map[v][j];
				pre[j]=v;
			}
		}
	}
}
int main()
{
	memset(Map,-1,sizeof(Map));
	n=read();
	for(int i=1;i<=n;i++)
	{
		V[i].x=read();
		V[i].y=read();
	}
	m=read();
	for(int i=1;i<=m;i++)
	{
		x=read();y=read();
		Map[x][y]=Map[y][x]=0;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(Map[i][j]==-1)
			{
				int dist=(V[i].x-V[j].x)*(V[i].x-V[j].x)+(V[i].y-V[j].y)*(V[i].y-V[j].y);
				Map[i][j]=Map[j][i]=dist;
			}
		}
	}
//	cout<<"start"<<endl;
	prim();
//	cout<<MST<<endl;
	return 0;
}

完成時間:(POJ又掛了,未AC)

方法一:暴力寫法
/*
	數位DP模板題
	但是看資料範圍,很明顯是一道水題啊。
	兩種方法都做一下。
*/
#include<iostream>
using namespace std;
const int N = 1000005;
int f[N];
bool check(int x)
{
	int flag=0;
	while(x)
	{
		if(x%10==4) return true;
		if(flag&&x%10==6) return true;
		if(x%10==2) flag=1;
		else flag=0;
		x/=10;
	}
	return false;
}
void init()
{
	for(int i=1;i<N;i++)
	{
		if(check(i)) f[i]=f[i-1];
		else f[i]=f[i-1]+1;
	}
}
int main()
{
	init();
	int a,b;
	while(cin>>a>>b)
	{
		if(a==0&&b==0) break;
		cout<<f[b]-f[a-1]<<endl;
	}
	return 0;
}
方法二:dfs寫法
#include<iostream>
using namespace std;
const int N = 15;
int dp[N][2];	//	dp[i][0/1]表示長度為i,末尾是否為6
int bit[N];
int dfs(int len,bool is6,int isMax)
{
	if(len==0) return 1;
	if(!isMax&&dp[len][is6]>0) return dp[len][is6];
	int cnt=0;
	int maxNum=isMax?bit[len]:9;
	for(int i=0;i<=maxNum;i++)
	{
		if(i==4) continue;
		if(i==2&&is6) continue;
		cnt+=dfs(len-1,i==6,isMax&&i==maxNum);
	}
	if(!isMax) dp[len][is6]=cnt;
	return cnt;
}
int solve(int x)
{
	int len=0;
	while(x)
	{
		bit[++len]=x%10;
		x/=10;
	}
	return dfs(len,false,true);
}
int main()
{
	int a,b;
	while(cin>>a>>b)
	{
		if(a==0&&b==0) return 0;
		cout<<solve(b)-solve(a-1)<<endl;
	}
	return 0;
}
方法三:遞推式寫法
/*
	dp[i][j]表示長度為i,首位為j的所有方案數
	需要注意,首位可以是0.
	預處理出所有的長度,計算即可。
*/
#include<iostream>
using namespace std;
const int N = 15;
int dp[N][N];
int bit[N];
void init()
{
	dp[0][0]=1;
	for(int i=1;i<N;i++)
		for(int j=0;j<=9;j++)
			for(int k=0;k<=9;k++)
				if(j!=4&&!(j==6&&k==2))
					dp[i][j]+=dp[i-1][k];
}
int solve(int x)
{
	int ans=0,len=0;
	while(x)
	{
		bit[++len]=x%10;
		x/=10;
	}
	bit[len+1]=0;
	for(int i=len;i>=1;i--)
	{
		for(int j=0;j<bit[i];j++)
		{
			if((bit[i+1]!=6||bit[i]!=2))
			{
				ans+=dp[i][j];
			}
		}
		if(bit[i]==4||(bit[i+1]==6&&bit[i]==2)) break;
	}
	return ans;
}
int main()
{
	int a,b;
	init();
	while(cin>>a>>b)
	{
		if(a==0&&b==0) return 0;
		cout<<solve(b+1)-solve(a)<<endl;	//	這裡需要注意一下
	}
}

完成時間:

WC2016年的題目? 好吧線段樹題,有些思維難度。
程式碼自己寫。。。
哈哈,好吧,其實是我自己寫了一大半,然後懵了。。。就不想寫了。。。

完成時間:

/*
	這是一道簡單得思維題,CQOI2007
	推算:k%i=k-[k/i]*i
		sum=sig(k%i)=sig(k-[k/i]*i)=nk-sig([k/i]*i);
	k整除i,是這道題目的突破口
*/
#include<iostream>
using namespace std;
int main()
{
	long long n,k,r,d;
	cin>>n>>k;
	long long ans=0;
	if(n>k) ans=(n-k)*k,n=k;
	for(long long l=1;l<=n;l=r+1)
	{
		d=k/l;
		r=k/d;	//	右區間,l為左區間
		if(r>n)r=n;
		ans+=(r-l+1)*k-(r-l+1)*(l+r)/2*d;
	}
	cout<<ans<<endl;
	return 0;
}

完成時間:

/*
	很顯然,設總共跳了c次相遇,
	則:我們可以寫出等式(mc+x)%L=(nc+y)%L;
	令總共差了k圈
	則x+mc=y+nc+kL;
	整理可得:(n-m)*c+Lk=x-y;求解最小的c
	歷史總時驚人的相似:ax+by=c;求解x
*/
#include<iostream>
using namespace std;
long long exgcd(long long a,long long b,long long &x,long long &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	long long c=exgcd(b,a%b,x,y);
	long long tmp=x; x=y; y=tmp-a/b*y;
	return c;
}
int main()
{
	long long x,y,n,m,L;
	cin>>x>>y>>m>>n>>L;
	long long a=n-m;
	long long b=L;
	long long c=x-y;
	long long gcd=exgcd(a,b,x,y);
	if(c%gcd) cout<<"Impossible"<<endl;
	else
	{
		x=x*(c/gcd);
		long long t=b/gcd;	//	通解x=x+t*k;
		x=(x%t+t)%t;	//	最小正整數
		cout<<x<<endl;
	}
	return 0;
}

完成時間:

思路有了,沒時間寫了。
最小生成樹題。

完成時間: