题目链接:https://vjudge.net/contest/153124
由于是中文题题意就不放了……
A题:
思路:每个点与(0,0)连线的中间点的个数是gcd(a,b),直接暴力会超时,所以换种思路:记录f[i]为gcd=i的个数,f[i]=(n/i)*(m/i)-i的倍数,之后就可以计算啦。
1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 6 typedef long long ll; 7 8 const int maxn=1e5+10; 9 10 ll f[maxn]; 11 12 int main(){ 13 ll n,m; 14 ll ans=0; 15 cin>>n>>m; 16 if(n<m) swap(n,m); 17 for(ll i=n;i>=1;i--){ 18 f[i]=(n/i)*(m/i); 19 for(ll j=2*i;j<=n;j+=i) 20 f[i]-=f[j]; 21 ans+=f[i]*(2*i-1); 22 } 23 cout<<ans<<endl; 24 return 0; 25 }
B题:
思路:已知图的度序列,判断是否可图化。用havel定理判断,不可图化就输出NO,可图化就输出YES和邻接矩阵,由于此题是Special Judge所以只用输出一种邻接矩阵就行啦。
havel定理:给定一个非负整数序列{dn},若存在一个无向图使得图中各点的度与此序列一一对应,则称此序列可图化。进一步,若图为简单图,则称此序列可简单图化。把序列排成不增序,即d1>=d2>=……>=dn,则d可简单图化当且仅当d’={d2-1,d3-1,……d(d1+1)-1, d(d1+2),d(d1+3),……dn}可简单图化。简单的说,把d排序后,找出度最大的点(设度为d1),把它与度次大的d1个点之间连边,然后这个点就可以不管了,一直继续这个过程,直到建出完整的图,或出现负度等明显不合理的情况。
实例:4 3 1 5 4 2 1和4 3 1 4 2 0是否可图化?
第一组数据,排序后为 5 4 4 3 2 1 1,出度最大的点为5,与度次大的点连边(4 4 3 2 1),之后去掉该点,序列排序后为 3 3 2 1 1 0,以此类推,2 1 1 0 0,0 0 0 0,即该度序列可以图化。
第二组数据,排序后为4 4 3 2 1 0,连边、去点、排序后为3 2 1 0 0,1 0 0 -1,出现负度,所以该度序列不可图化。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <vector> 5 #include <cstring> 6 7 using namespace std; 8 9 const int maxn=15; 10 11 typedef pair<int,int> P; 12 13 vector<P> v; 14 int mp[maxn][maxn]; 15 16 int cmp(const P &a,const P &b){ 17 return a.first>b.first; 18 } 19 20 int n; 21 22 bool judge(){ 23 while(!v.empty()){ 24 sort(v.begin(),v.end(),cmp); 25 if(v[0].first>=v.size()) return false; 26 for(int i=1;i<=v[0].first;++i){ 27 --v[i].first; 28 if(v[i].first<0) return false; 29 mp[v[0].second][v[i].second]=mp[v[i].second][v[0].second]=1; 30 } 31 v.erase(v.begin()); 32 } 33 return true; 34 } 35 36 int main(){ 37 ios::sync_with_stdio(false); 38 cin.tie(0); 39 int T; 40 cin>>T; 41 while(T--){ 42 v.clear(); 43 memset(mp,0,sizeof(mp)); 44 cin>>n; 45 for(int i=0;i<n;i++){ 46 int tem;cin>>tem; 47 v.push_back(make_pair(tem,i)); 48 } 49 bool right=judge(); 50 if(right){ 51 cout<<"YES"<<endl; 52 for(int i=0;i<n;i++){ 53 for(int j=0;j<n;j++){ 54 cout<<mp[i][j]<<" "; 55 } 56 cout<<endl; 57 } 58 } 59 else cout<<"NO"<<endl; 60 cout<<endl; 61 } 62 return 0; 63 }
C题:
思路:优先队列模拟。按优先级从小到大,优先级相等的情况下起始时间从大到小放入优先队列,对于第i个进程,如果i的起始时间与i-1的结束时间的时间差能够被优先队列中的堆顶进程完全使用(即堆顶进程的持续时间len<=时间差),则该进程的结束时间在这个时间差内,否则len-时间差并重新放回优先队列中。
1 #include <iostream> 2 #include <queue> 3 #include <algorithm> 4 #include <cstring> 5 #include <cstdio> 6 7 using namespace std; 8 9 typedef pair<int,int> P; 10 11 const int maxn=3e5+10; 12 const int inf=1e9; 13 14 int start[maxn],slen[maxn],prv[maxn]; 15 16 struct node{ 17 int id,len; 18 bool operator <(const node&a) const{ 19 if(prv[id]==prv[a.id]) return start[id]>start[a.id]; 20 return prv[id]<prv[a.id]; 21 } 22 }; 23 24 P ans[maxn]; 25 26 priority_queue<node> q; 27 28 int cmp(const P&a,const P&b){return a.second<b.second;} 29 30 int main(){ 31 int id; 32 while(scanf("%d",&id)!=EOF){ 33 scanf("%d %d %d",&start[id],&slen[id],&prv[id]); 34 ans[id].first=id; 35 } 36 start[id+1]=inf; 37 int now=0; 38 for(int i=1;i<=id+1;i++){ 39 int len=start[i]-now; 40 while(!q.empty()&&q.top().len<=len){ 41 now+=q.top().len; 42 ans[q.top().id].second=now; 43 len-=q.top().len; 44 q.pop(); 45 } 46 if(i==id+1) break; 47 if(!q.empty()&&len){ 48 node tmp=q.top();q.pop(); 49 tmp.len-=len; 50 q.push(tmp); 51 } 52 now=start[i]; 53 q.push((node){i,slen[i]}); 54 } 55 sort(ans+1,ans+id+1,cmp); 56 for(int i=1;i<=id;i++){ 57 printf("%d %d ",ans[i].first,ans[i].second); 58 } 59 return 0; 60 }
D题:
思路:LIS(最长上升子串,动态规划)的做法,如果i能够被j(i>j)在限时内抵达,则i可以被j转移。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 using namespace std; 6 7 const int maxn=10005; 8 9 int dp[maxn],x[maxn],y[maxn],t[maxn]; 10 11 int main(){ 12 int n,m; 13 scanf("%d %d",&n,&m); 14 for(int i=1;i<=m;i++){ 15 scanf("%d %d %d",&t[i],&x[i],&y[i]); 16 } 17 dp[1]=1;int ans=1; 18 for(int i=2;i<=m;i++){ 19 dp[i]=1; 20 for(int j=1;j<=i-1;j++){ 21 if(abs(x[i]-x[j])+abs(y[i]-y[j])<=t[i]-t[j])dp[i]=max(dp[i],dp[j]+1); 22 } 23 ans=max(ans,dp[i]); 24 } 25 printf("%d ",ans); 26 return 0; 27 }
E题:
思路:由于1e6的数据量,可以从小到大枚举M,判断同余方程Pi-Pj≡Cj-Ci(mod M)是否成立,如果对于每组i,j都不成立,则M为最小的整数解。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 using namespace std; 6 7 const int maxn=100; 8 9 int exgcd(int a,int b,int &x,int &y){ 10 if(b==0){x=1,y=0;return a;} 11 int d=exgcd(b,a%b,y,x); 12 y-=a/b*x; 13 return d; 14 } 15 16 int C[maxn],P[maxn],L[maxn]; 17 int n; 18 19 bool solve(int b){ 20 for(int i=1;i<n;i++){ 21 for(int j=i+1;j<=n;j++){ 22 int a=((P[i]-P[j])%b+b)%b,m=((C[j]-C[i])%b+b)%b,x,y,d; 23 d=exgcd(a,b,x,y); 24 if(m%d!=0) continue; 25 m/=d; 26 int bb=b/d; 27 int num=(m*x%bb+bb)%bb; 28 if(num<=min(L[i],L[j])) return false; 29 } 30 } 31 return true; 32 } 33 34 int main(){ 35 int mx=0; 36 cin>>n; 37 for(int i=1;i<=n;i++){ 38 cin>>C[i]>>P[i]>>L[i]; 39 mx=max(C[i],mx); 40 C[i]--; 41 } 42 for(int i=mx;;i++){ 43 if(solve(i)){ 44 cout<<i<<endl; 45 break; 46 } 47 } 48 return 0; 49 }