概述
1009 Sparse Graph(hdu5876)
由于每条边的权值都为1,所以最短路bfs就够了,只是要求转置图的最短路,所以得用两个set来维护,一个用来存储上次扩散还没访问的点,一个用来存储这一次扩散还没访问的点。
算法:bfs+set
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<string.h> 5 #include<queue> 6 #include<vector> 7 #include<stack> 8 #include<set> 9 using namespace std; 10 const int maxn=200100; 11 const int INF=0x3f3f3f3f; 12 int t,n,m,u,v; 13 int head[maxn];int tot; 14 int di[maxn]; 15 struct Edge 16 { 17 int to,next; 18 }edge[maxn]; 19 void init() 20 { 21 tot=0; 22 memset(head,-1,sizeof(head)); 23 } 24 void add(int u,int v) 25 { 26 edge[tot].to=v; 27 edge[tot].next=head[u]; 28 head[u]=tot++; 29 } 30 struct node 31 { 32 int num,dis; 33 }; 34 35 void bfs(int beg) 36 { 37 set<int>s,e; 38 set<int>::iterator it; 39 queue<node>q; 40 for(int i=1;i<=n;i++) 41 s.insert(i),di[i]=INF; 42 node temp,nex; 43 temp.num=beg,temp.dis=0; 44 q.push(temp); 45 s.erase(beg); 46 while(!q.empty()) 47 { 48 temp=q.front();q.pop(); 49 for(int i=head[temp.num];i!=-1;i=edge[i].next) 50 { 51 int v=edge[i].to; 52 if(s.find(v)==s.end()) 53 continue; 54 s.erase(v);e.insert(v); 55 } 56 for(it=s.begin();it!=s.end();it++) 57 { 58 nex.num=*it;nex.dis=temp.dis+1; 59 di[nex.num]=min(nex.dis,di[nex.num]); 60 q.push(nex); 61 } 62 s.swap(e);e.clear(); 63 } 64 } 65 int main() 66 { 67 //freopen("input.txt","r",stdin); 68 scanf("%d",&t); 69 while(t--) 70 { 71 scanf("%d%d",&n,&m); 72 init(); 73 for(int i=0;i<m;i++){ 74 scanf("%d%d",&u,&v); 75 add(u,v); 76 add(v,u); 77 } 78 int pos; 79 scanf("%d",&pos); 80 bfs(pos); 81 if(n!=pos){ 82 for(int i=1;i<=n;i++) 83 { 84 if(i==pos)continue; 85 if(i!=n) 86 printf("%d ",di[i]!=INF?di[i]:-1); 87 else 88 printf("%dn",di[i]!=INF?di[i]:-1); 89 } 90 91 }else{ 92 for(int i=1;i<=n-2;i++) 93 { 94 if(1!=(n-1)) 95 printf("%d ",di[i]!=INF?di[i]:-1); 96 else 97 printf("%dn",di[i]!=INF?di[i]:-1); 98 } 99 100 } 101 } 102 return 0; 103 }
1010 Weak Pair(hdu5877)
题意是要求所有节点的权值与其祖先节点权值相乘小于或等于k的总共有多少对,一开始知道必须得进行dfs,也知道可以马上建一个数组b[n]来记录每个节点需要和小于多少的数相乘才会小于看,但是在查询有多少个祖先节点小于b[i]时没有找到方法,后来才知道,可以将数据离散化后存进树状数组,只保留当前节点的父节点在树状数组当中,一旦离开这个节点,就把这个节点的权值在树状数组中删除
算法:dfs+离散化+BIT
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<string.h> 5 #include<vector> 6 #include<queue> 7 #include<stack> 8 using namespace std; 9 #define ll long long 10 const int maxn=100100; 11 int n; 12 ll k,a[maxn]; 13 vector<int>son[maxn]; 14 ll ran[maxn]; 15 ll b[maxn]; 16 int in[maxn]; 17 ll B[maxn]; 18 int low_bit(int x) 19 { 20 return x&(-x); 21 } 22 ll Sum(int x){ 23 ll s1 = 0; 24 while (x) s1 += B[x], x -= low_bit(x); 25 return s1; 26 } 27 void Add(int x, int d){ 28 while (x<=n) B[x] += d,x += low_bit(x); 29 } 30 void init(int n) 31 { 32 for(int i=1;i<=n;i++){ 33 son[i].clear(); 34 } 35 memset(in,0,sizeof(in)); 36 } 37 ll dfs(int rt) 38 { 39 Add(ran[rt],1); 40 ll ans=0; 41 for(int i=0;i<son[rt].size();i++){ 42 int nex=son[rt][i]; 43 ans+=dfs(nex); 44 } 45 Add(ran[rt], -1); 46 ll num=upper_bound(a+1,a+1+n,b[rt])-a-1; 47 ans+=Sum(num); 48 return ans; 49 } 50 int main() 51 { 52 //freopen("input.txt","r",stdin); 53 int t; scanf("%d", &t); 54 while(t--) 55 { 56 scanf("%d%lld",&n,&k); 57 init(n); 58 for(int i=1;i<=n;i++){ 59 scanf("%lld",&a[i]); 60 ran[i] = a[i]; 61 if (a[i] != 0) 62 b[i] = k / a[i]; 63 else 64 b[i] = 0; 65 } 66 67 int u,v; 68 for(int i=1;i<=n-1;i++){ 69 scanf("%d%d",&u,&v); 70 son[u].push_back(v); 71 in[v]++; 72 } 73 sort(a+1,a+1+n); 74 unique(a+1,a+1+n); 75 for(int i=1;i<=n;i++){ 76 ran[i]=lower_bound(a+1,a+n+1,ran[i])-a; 77 } 78 int root=1; 79 for(int i=1;i<=n;i++){ 80 if(in[i]==0) 81 root=i; 82 } 83 ll ans=dfs(root); 84 printf("%lldn",ans); 85 } 86 return 0; 87 }
1001 Different Circle Permutation
题意:1条项链有n个珠子,有黑白2种颜色,求任意2个黑珠不相邻的项链的种类数(旋转同构)
由于有黑珠不能相邻的条件,burnside或polya不能套用。不过自己计算不考虑旋转同构的方案数就会发现:
设f(n)为n个珠子涂色使得黑珠左右不相邻的方案数,则f(n)=f(n-1)+f(n-2)其中f(1)=1 f(2)=3
可以理解为前者是1个白珠子接上n-1个珠子,后者是1个黑珠子接上1个白珠子再接n-2个珠子(出现最右1个是黑珠的情况,把最左黑珠与右边互换,还是不同方案)
至于如何求旋转同构时的方案数,把n个珠子按照1,2,3,4...u,1...这个编号平均分成n/u份,每份都是按照原先左右关系连接后满足黑珠不相邻的,所以这部分方案数是f(u)
要满足这个,首先n%u==0,那旋转k个珠子的角度实际上是分割为每份gcd(k,n)
根据burnside引理,n种珠子排布,在k种旋转后相同视为同构,方案数是(这n种珠子排布分别做k种旋转后还是同样排布的数量之和)/k
所以方案数是∑(f( gcd(i,n) ))/n ( 1<=i<=n)
思维出来了,程序也容易写了
由于n可达10的9次方,暴力计算会超时
其实可以合并的,根据欧拉函数的定义可以合并为(1<=i<=n)∑(f( gcd(i,n) ))/n=(d|n)∑f(d)*euler(n/d)
f(d)可以矩阵快速幂得出,euler(d)由于实在太大应该分解质因子做
其实这2种运算都可以在一个合理的范围内打表,小的直接查表得出
欧拉函数也用不着分解到底
算法:矩阵幂+欧拉函数运算
#include <bits/stdc++.h> using namespace std; const long long mod = 1e9+7 ; struct matrix { long long x1,x2 ; long long x3,x4 ; }; matrix mul(matrix a,matrix b){ matrix ans ; ans.x1 = (a.x1*b.x1 + a.x2*b.x3)%mod ; ans.x2 = (a.x1*b.x2 + a.x2*b.x4)%mod ; ans.x3 = (a.x3*b.x1 + a.x4*b.x3)%mod ; ans.x4 = (a.x3*b.x2 + a.x4*b.x4)%mod ; return ans ; } long long quick_matrix(long long x){ x -= 4 ; matrix ans,cal ; ans.x1 = ans.x2 = ans.x3 = 1 ; ans.x4 = 0 ; cal.x1 = cal.x2 = cal.x3 = 1 ; cal.x4 = 0 ; while (x){ if (x%2) ans = mul(ans,cal) ; cal = mul(cal,cal) ; x >>= 1 ; } return (ans.x1*4+ans.x2*3)%mod ; } long long fx(long long x){ if (x == 1) return 1; else if (x == 2) return 3; else if (x == 3) return 4; else return quick_matrix(x) ; } long long quick(long long a,long long n){ long long ans = 1 ; long long cal = a ; while (n){ if (n%2) ans = (ans*cal)%mod ; cal = (cal*cal)%mod; n >>= 1; } return ans ; } long long euler(long long n) { long long ans = n; long long i; for (i = 2; i*i <= n; i++){ if (n%i == 0){ while (n%i == 0) n /= i; ans = ans/i*(i-1) ; } } if (n != 1) ans = ans/n*(n-1); return ans; } long long solve(long long n){ if (n == 1) return 2; long long ans = 0; long long nn = n ; long long d; long long i; for (i = 1; i*i < n; i++){ if (n%i == 0){ ans = (ans + fx(i)*euler(nn/i) + fx(nn/i)*euler(i))%mod ; } } if (i*i == n) ans = (ans + fx(i)*euler(i))%mod ; return (ans*quick(nn,mod-2))%mod; } int main() { long long n; while (~scanf("%lld",&n)) printf("%lldn",solve(n)) ; return 0 ; }
1002 Different GCD Subarray Query
题意:求Q个区间含有不同的连续序列gcd个数,例如(6,8,12) gcd(6)=6 gcd(8)=8 gcd(12)=12 gcd(6,8)=2 gcd(8,12)=4 gcd(6,8,12)=2 含5种gcd值
固定右边界,左边界越往左,连续序列gcd值越小,并且两个数要么相等,要么至少相差1倍,所以一个右边界最多延伸出logn种gcd值。
题目可以离线做,合并相同右边界的查询,预处理出n个右边界的逆序对应gcd值。
树状数组储存最右出现的gcd值种类数
查询时把相同gcd值最先出现的位置尽量往右移,这样区间查询就不会减去[1,l-1]和[l,r]都含有的并且最先出现在[l,r]的数字
算法:离线+BIT
#include <cstdio> #include <iostream> #include <cstring> #include <vector> #include <stack> #include <set> #include <queue> #include <algorithm> #define lowbit(x) (x & (-x)) using namespace std; typedef __int64 ll; typedef pair<int,int> pii; const int MAXN = 1e5 + 8; int a[MAXN]; int ans[MAXN]; vector<pii> gc[MAXN]; vector<pii> query[MAXN]; int vis[MAXN * 10]; int n,q; int __gcd(int a,int b){return b?__gcd(b,a%b):a;} int sum[MAXN]; void treeinit(){memset(sum,0,sizeof(sum));} void update(int x,int v){while(x <= n){sum[x] += v;x += lowbit(x);}} int getSum(int x){int ans = 0;while(x){ans += sum[x];x -= lowbit(x);}return ans;} void init(){ for (int i = 0;i <= n;i ++){ gc[i].clear(); query[i].clear(); } memset(vis,0,sizeof(vis)); treeinit(); } void input(){ int i,j; for (i = 1;i <= n;i ++) scanf("%d",a + i); //统计不同右边界从右到左的连续相同gcd值,O(nlog2n) //这里还有一种方法,预先处理出RMQ再O(nlog2nlog2n)二重二分 for (i = 1;i <= n;i ++){ int x = a[i]; int y = i; for (j = 0; j < gc[i - 1].size();j ++){ int res = __gcd(gc[i - 1][j].first,x); if (x != res){ gc[i].push_back(make_pair(x,y)); x = res; y = gc[i - 1][j].second; } } gc[i].push_back(make_pair(x,y)); } for (i = 1;i <= q;i ++){ int l,r; scanf("%d%d",&l,&r); query[r].push_back(make_pair(l,i)); } //查询区间不同数的数量,右边界到哪加到哪 for (i = 1;i <= n;i ++){ //这里的思路,不是想着把查询分成1个三角形和一个直角梯形(事实证明这个思路有些复杂) //是把不同种类的数映射位置后放在数组上(若有重复则把数右移),以备区间查询 for (j = 0;j < gc[i].size();j ++){ int res = gc[i][j].first; int ind = gc[i][j].second; if (vis[res])//有相等的数则把数的头标签往右移 update(vis[res],-1); vis[res] = ind; update(ind,1); } for (j = 0;j < query[i].size();j ++){ int l = query[i][j].first; int ind = query[i][j].second; ans[ind] = getSum(i) - getSum(l - 1); } } for (i = 1;i <= q;i++){ printf("%dn",ans[i]); } } int main(){ while(scanf("%d%d",&n,&q)!=EOF){ init(); input(); } return 0; }
1005 Seats
题意:有m个部门(部门数和分别的学生数不确定),部门学生数不确定,但一定不大于h,整个学校学生数L人,会场一排k个座位且L%k==0。问在任何情况下都能让同部门的学生一定坐一排的最少座位排数。
要让这些学生占据最多的行,就是说每行座位的空位正好无法再坐一个部门,比较简单的做法:先求出部门数较多的情况下空位最少时,每排部门数=k/h,那要让空位最多,并且让1个部门学生多得不能再坐,那能坐满排时1个部门学生数=k/(k/h+1),让1排空出最多的位需要1个部门有且仅有r=k/(k/h+1)+1,那样每排部门数又是k/h,每排最少要排k-k%r,剩下的人可以另起1行或安排到其他排的空位上(但是他喵的数据错误了,后者的情况下把人塞到其他排会WA)
#include <bits/stdc++.h> #define INF 0x3f3f3f3f #define MOD 1000000007 #define N 1123456 using namespace std; long long n,m,sum,res,flag; int main() { #ifndef ONLINE_JUDGE freopen("test.txt","r",stdin); #endif long long i,j,cas,T,t,x,y,z,h,l,k; while(scanf("%I64d%I64d%I64d",&h,&l,&k)!=EOF) { x=k/h;//最大人数时一排几个部门 y=k/(x+1);//多放一个部门时每个部门最多的人数 res=y+1;//加一个人保证不能多放一个部门 printf("%I64dn",l/(k-k%res)+bool(l%(k-k%res)));//不是正解,数据正确的话会WA } return 0; }
1006 football game
题意不多说。
定理什么的,可以理解为n个人比赛,胜得2分平得1分,那n人总分就是n(n-1)
排序,再计算前q人的总分,由于没人的分数是负数,如果前q人总分超q(q-1)就不合理
最后的总分也必须是n(n-1)
#include<bits/stdc++.h> using namespace std; const int maxn = 20000+6; int n,a[maxn]; int main() { int T; while(scanf("%d",&T)!=EOF) { while(T--) { int res = 0; scanf("%d",&n); for(int i = 1;i<=n;i++) scanf("%d",&a[i]),res+=a[i]; sort(a+1,a+1+n); int sum = 0; int flag = 0; for(int i = 1;i<=n;i++) { sum+=a[i]; if(sum<i*(i-1)) { flag=1; break; } } if(res!=n*(n-1)) flag=1; if(flag) printf("Fn"); else printf("Tn"); } } }
1007 friends and emenies
脑洞多一点的人会想到把互为朋友的1对关系当成1对连边
如果3人互为朋友,那为3人的3对关系找3种珠子不如给3人1种珠子,相当于三元环
所以问题其实是n个点,没有三元环并且互相连通的图最多边数
没有三元环,就是说可以形成二分图;最多边数,就是说2个点集点数相差最小
边数n/2*(n-n/2)
再比较下实际有的珠子种类数就ok了
#include <bits/stdc++.h> using namespace std; typedef long long LL; int main() { LL m,n; while(~scanf("%I64d %I64d",&m,&n)) { LL ans = m/2*(m-m/2); if( ans <= n) puts("T"); else puts("F"); } return 0; }
1008 function
与1002差不多,都是那种1个左(右)边界对应logn种(m%n可能是m,也可能是小于m/2的某个数)数的题目
还是一样,离线查询,把所有的区间求出来,注意合并
不过这次不用事先求出所有对
并且储存方式也改为优先队列以防止多余处理(能模到小于原数的要入队,不能的不处理)
在右边界向右推进的同时更新对应左边界对应数
算法:离线+优先队列
#include <bits/stdc++.h> using namespace std; typedef unsigned long long ULL; typedef long long LL; const int INF = 0x3f3f3f3f; const double eps = 1e-9; const int maxn = 100000 + 100; typedef pair<int, int> Pair; int ans[maxn], L[maxn]; vector<Pair> R[maxn]; //(L, id) priority_queue<Pair> cal; //(num, pos) int main() { int T, cas=1, n; scanf("%d", &T); while(T--) { while(!cal.empty()) cal.pop(); for(int i=0;i<maxn;++i) R[i].clear(); scanf("%d", &n); for(int i=1;i<=n;++i) scanf("%d", &L[i]); int l, r, q; scanf("%d", &q); for(int i=1;i<=q;++i)//把所有右边界相等的元素归一起,这是只有区间查询时的常用策略 { scanf("%d%d", &l, &r); R[r].push_back(make_pair(l, i)); } for(int i=1;i<=n;++i) { int l, r; //处理能改变结果的查询,用堆防止过多访问变成O(n^2) //注意1个左边界最多得到O(log2n)种结果 while(!cal.empty()&&cal.top().first>=L[i]) { l = cal.top().second; r = i; L[l] = cal.top().first%L[i]; cal.pop(); cal.push(make_pair(L[l], l)); } cal.push(make_pair(L[i], i)); for(int j=0;j<R[i].size();++j) { l = R[i][j].first; ans[R[i][j].second] = L[l]; } } for(int i=1;i<=q;++i) printf("%dn", ans[i]); } return 0; }
转载于:https://www.cnblogs.com/dgutfly/p/5874406.html
最后
以上就是矮小摩托为你收集整理的2016 ACM/ICPC Asia Regional Dalian Online1009 Sparse Graph(hdu5876)1010 Weak Pair(hdu5877)的全部内容,希望文章能够帮你解决2016 ACM/ICPC Asia Regional Dalian Online1009 Sparse Graph(hdu5876)1010 Weak Pair(hdu5877)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复