20181214第一週周訓思路整理
阿新 • • 發佈:2018-12-30
第一週題解
訓練時間:2018/12/14-2018/12/21 author:wlxsq
訓練時間:2018/12/14-2018/12/21 author:wlxsq
Problem List
- BZOJ4300: 絕世好題 Problem link
- Bailian1308: Is It A Tree? Problem link
- HDU2594: Simpsons’ Hidden TalentsProblem link
- POJ1511:Invitation Cards Problem link
- POJ1751:HighwaysProblem link
- HDU2089:不要62
- BZOJ4355:Play with SequenceProblem link
- BZOJ1257:餘數之和Problem link
- POJ2061:青蛙的約會Problem link
- BZOJ1601:[N/A]Problem link
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
- POJ1751:Highways連結
/*
最小生成樹題
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)
- HDU2089:不要62連結
方法一:暴力寫法
/*
數位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; // 這裡需要注意一下
}
}
完成時間:
- BZOJ4355:Play with SequenceClick me
WC2016年的題目? 好吧線段樹題,有些思維難度。
程式碼自己寫。。。
哈哈,好吧,其實是我自己寫了一大半,然後懵了。。。就不想寫了。。。
完成時間:
- BZOJ1257:餘數之和Click me
/*
這是一道簡單得思維題,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;
}
完成時間:
- POJ2061:青蛙的約會Click me
/*
很顯然,設總共跳了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;
}
完成時間:
- BZOJ1601:[N/A]Click me
思路有了,沒時間寫了。
最小生成樹題。
完成時間: