【题解】 2016 ACM/ICPC Asia Regional Dalian Online (2+5)
来源:互联网 发布:nginx 设置访问密码 编辑:程序博客网 时间:2024/05/04 13:28
赛时只出了两题 09 10
06卡死了。很烂,没有及时弃疗06,最后得不偿失
丢人。
1001 Different Circle Permutation(矩快+polya+欧拉函数)
题目大意:n个座位围成一圈,n个人中挑出若干个人坐下,要求相邻座椅不可同时有人。n个座位均匀分布,即相邻座位与圆心夹角为
问题可转化为环上n个点涂色,黑白两色,要求相邻点不可同时为黑。
若不考虑相邻不可同为黑,就是经典的染色问题,旋转相同为同构,对称相同不算同构。
polya定理可解,为1/|G|*(mC(π1)+mC(π2)+mC(π3)+…+mC(πk)).
由于n很大,所以用欧拉函数求gcd。
即
此上是考虑黑白随意涂色。
题目限制黑色不可相邻。其实对于给定的n,f(n) = f(n-1)+f(n-2)
设黑色为1,白色为0
考虑新加点为0 f(n-1)合法,则新加点两边状态为 00 01 10
可组成 (0 0 0) (0 0 1) (1 0 0)
对于f(n-2) n的基础上扣去两个点,两边状态为 00 01 10
则可组成 (0 10 0) (0 10 1) (1 01 0)
至此对于n个点的所有情况都考虑到了。
这样带入之前公式。即为
枚举n的因子,在线求欧拉函数,乘上矩快求出的方案数即可。
代码如下:
#include <iostream>#include <cmath>#include <vector>#include <cstdlib>#include <cstdio>#include <climits>#include <ctime>#include <cstring>#include <queue>#include <stack>#include <list>#include <algorithm>#include <map>#include <set>#define LL long long#define Pr pair<int,int>#define fread(ch) freopen(ch,"r",stdin)#define fwrite(ch) freopen(ch,"w",stdout)using namespace std;const int INF = 0x3f3f3f3f;const int msz = 10000;const int mod = 1e9+7;const double eps = 1e-8;LL Pow_m(LL a,int b){ LL ans = 1; while(b) { if(b&1) ans = (ans*a)%mod; b >>= 1; a = (a*a)%mod; } return ans;}LL eular(LL n){ LL ans = n; for(int i = 2; i*i <= n; ++i) { if(n%i) continue; ans -= ans/i; while(n%i == 0) n /= i; } if(n > 1) ans -= ans/n; return ans;}struct Matrix{ LL ans[3][3]; void init() { memset(ans,0,sizeof(ans)); ans[0][0] = ans[0][1] = ans[1][0] = ans[2][1] = 1; } void init(int pos) { memset(ans,0,sizeof(ans)); for(int i = 0; i < 3; ++i) ans[i][i] = 1; } Matrix operator *(const struct Matrix a)const { Matrix tmp; memset(tmp.ans,0,sizeof(tmp.ans)); for(int i = 0; i < 3; ++i) for(int j = 0; j < 3; ++j) for(int k = 0; k < 3; ++k) tmp.ans[i][j] = (tmp.ans[i][j]+ans[i][k]*a.ans[k][j]%mod)%mod; return tmp; } void prt() { for(int i = 0; i < 3; ++i) { for(int j = 0; j < 3; ++j) { printf("%lld ",ans[i][j]); } puts(""); } }};Matrix ans,a;LL f(int b){ if(b == 1) return 1; if(b == 2) return 3; b -= 3; ans.init(1); a.init(); while(b) { if(b&1) ans = ans*a; b >>= 1; a = a*a; } //ans.prt(); return (4*ans.ans[0][0]+3*ans.ans[0][1]+ans.ans[0][2])%mod;}LL solve(int n){ if(n == 1) return 2; LL ans = 0; LL d; for(d = 1; d*d < n; ++d) { if(n%d) continue; ans = (ans+eular(d)*f(n/d)%mod+eular(n/d)*f(d)%mod)%mod; } if(d*d == n) ans = (ans+eular(d)*f(d)%mod)%mod; return ans*Pow_m(n,mod-2)%mod;}int main(){ //fread(""); //fwrite(""); int n; while(~scanf("%d",&n)) { printf("%lld\n",solve(n)); } return 0;}
1002 Different GCD Subarray Query(离线+树状数组)
题目大意:n个数,q次查询,每次查询[l,r]中不同的区间gcd个数。
fzu一场月赛里有,解法一样。当时还写了题解、!GG
看到其他巨巨有用两个vector搞的。考虑gcd的单调性,所以vector里类似一个单调栈,然后就保证了无重之类的。很赞。
我照着敲了,无限RE……后来改成一种map的写法。还是RE……再后来自己跑数据,测出sort挂了……cmp函数<=会挂,<即没事……
然后vector写法WA了……map写法A了……说说我的map思路吧,vector思路网上挺多了。感觉map比较好明白,不过就是时间可能差一点。
考虑把询问预存下来,按右边界排序,然后遍历右边界,过程中求出每个左边界到当前右边界的gcd种数,这样当前右边界的询问中每个左边界可以求一个答案出来。gcd种数可以用树状数组存。
那么存下每种gcd在当前右边界下,最近(最靠右)的左边界即可。
这就是用到map的地方,存储每种gcd的最大的左边界。
这里用到三个map
map1:遍历到当前位置,每种gcd的最大左边界
map2:遍历到当前位置i,右边界为i-1的gcd的最大左边界
map3:i-1 -> i的转移
类似dp的思想转移即可。
代码如下:
#include <iostream>#include <cmath>#include <vector>#include <cstdlib>#include <cstdio>#include <climits>#include <ctime>#include <cstring>#include <queue>#include <stack>#include <list>#include <algorithm>#include <map>#include <set>#define LL long long#define Pr pair<int,int>#define fread(ch) freopen(ch,"r",stdin)#define fwrite(ch) freopen(ch,"w",stdout)using namespace std;const int INF = 0x3f3f3f3f;const int msz = 112345;const int mod = 1e9+7;const double eps = 1e-8;int a[msz],n;int bit[msz];map <int,int> mp2;map <int,int> mp1;map <int,int> tmp;struct Query{ int l,r,id; bool operator <(const struct Query a)const { return r < a.r; }};Query que[msz];int ans[msz];int Lowbit(int x){ return x&(-x);}void Add(int x,int ad){ while(x <= n) { bit[x] += ad; x += Lowbit(x); }}int Sum(int x){ int ans = 0; while(x) { ans += bit[x]; x -= Lowbit(x); } return ans;}int main(){ //fread("in.in"); //fwrite(""); int q; while(~scanf("%d%d",&n,&q)) { memset(bit,0,sizeof(bit)); for(int i = 1; i <= n; ++i) scanf("%d",&a[i]); for(int i = 0; i < q; ++i) { scanf("%d%d",&que[i].l,&que[i].r); que[i].id = i; } sort(que,que+q); int pos = 0; int g; mp1.clear(); mp2.clear(); for(int i = 1; i <= n; ++i) { tmp.clear(); for(auto iter: mp2) { g = __gcd(iter.first,a[i]); if(!tmp.count(g) || tmp[g] < iter.second) tmp[g] = iter.second; } tmp[a[i]] = i; mp2.clear(); for(auto iter: tmp) { if(!mp1.count(iter.first)) { mp1[iter.first] = iter.second; Add(iter.second,1); } else if(mp1[iter.first] < iter.second) { Add(mp1[iter.first],-1); mp1[iter.first] = iter.second; Add(iter.second,1); } mp2[iter.first] = iter.second; } while(pos < q && que[pos].r == i) { ans[que[pos].id] = Sum(que[pos].r)-Sum(que[pos].l-1); pos++; } } for(int i = 0; i < q; ++i) printf("%d\n",ans[i]); } return 0;}
1006 Football Games(Landau’s Theorem)
题目大意:n支队伍两两比赛,赢得2分,输不得分,平局分别得1分
给出最终得分,问是否合法。
Landau’s Theorem的变形,赢得1分其余无分即为Landau’s Theorem。
具体证明没明白……判定方式就是从小到大排序。
对于此题,如果第i人赢得前面全部的,输掉后面全部的,最终所有队伍得分就会变成0 2 4 6 8 10这种情况。
那么
即对于此题 得分最少的i个人的分数总和的下界 为i(i-1)
代码如下:
#include <iostream>#include <cmath>#include <vector>#include <cstdlib>#include <cstdio>#include <climits>#include <ctime>#include <cstring>#include <queue>#include <stack>#include <list>#include <algorithm>#include <map>#include <set>#define LL long long#define Pr pair<int,int>#define fread(ch) freopen(ch,"r",stdin)#define fwrite(ch) freopen(ch,"w",stdout)using namespace std;const int INF = 0x3f3f3f3f;const int msz = 10000;const int mod = 1e9+7;const double eps = 1e-8;const int maxn = 233;int num[21234];bool judge(int n){ int ans = 0; for(int i = 0; i < n; ++i) { ans += num[i]; if(ans < (i+1)*i) return false; } return ans == n*(n-1);}int main(){ int t,n; while(~scanf("%d",&t)) { while(t--) { scanf("%d",&n); for(int i = 0; i < n; ++i) scanf("%d",&num[i]); sort(num,num+n); puts(judge(n)? "T": "F"); } } return 0;}
1007 Friends and Enemies(二分图完美匹配)
题目大意:
m个人 n种颜色石头。
人与人之间关系要么是朋友,要么是敌人,关系不具有传递性。
每个人可以携带任何数量任何种颜色的石头(也可以不带)
对于任何两个人,如果是朋友,携带的石头至少有一种相同颜色。
如果是朋友,携带的石头颜色必须完全不同。
问n种颜色的石头能不能满足所有关系下m个人佩戴的石头都符合要求。
就是找最坏条件下m个人需要的石头种类,跟n进行比较。
把m个人分成两组,每组内部都是敌人关系,两组间两两互为朋友。
这样所需要石头种数就是两组间的连线数,并且是最坏情况。
n/2*(n+1)/2
代码如下:
#include <iostream>#include <cmath>#include <vector>#include <cstdlib>#include <cstdio>#include <climits>#include <ctime>#include <cstring>#include <queue>#include <stack>#include <list>#include <algorithm>#include <map>#include <set>#define LL long long#define Pr pair<int,int>#define fread(ch) freopen(ch,"r",stdin)#define fwrite(ch) freopen(ch,"w",stdout)using namespace std;const int INF = 0x3f3f3f3f;const int msz = 10000;const int mod = 1e9+7;const double eps = 1e-8;const int maxn = 233;int main(){ LL n,m; while(~scanf("%lld%lld",&n,&m)) puts(m >= (n/2)*((n+1)/2)? "T": "F"); return 0;}
1008 Function(优先队列)
题目大意:
给出n个正整数和q次询问,每次询问[l,r] 输出
已知mod操作类似gcd操作,结果是单调的,只会小不会大。
把所有询问预存,按左边界排序,当前左边界存在于询问时,加入优先队列,对于当前位置,优先队列中大于a[i]的都对a[i]取余,取到 < a[i]即可停止,更小的肯定更无变化,根据右边界抛出即可
代码如下:
#include <iostream>#include <cmath>#include <vector>#include <cstdlib>#include <cstdio>#include <climits>#include <ctime>#include <cstring>#include <queue>#include <stack>#include <list>#include <algorithm>#include <map>#include <set>#define Pr pair<int,int>#define fread(ch) freopen(ch,"r",stdin)#define fwrite(ch) freopen(ch,"w",stdout)using namespace std;typedef long long LL;const int INF = 0x3f3f3f3f;const double eps = 1e-9;const int maxn = 100000 + 100;struct Query{ int l,r,x,id; bool operator >(const struct Query a)const { return x < a.x; }};int a[112345];Query que[113245];int ans[112345];bool cmp(Query a,Query b){ return a.l < b.l;}int main(){ int t,n,m; scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i = 1; i <= n; ++i) scanf("%d",&a[i]); scanf("%d",&m); for(int i = 0; i < m; ++i) { scanf("%d%d",&que[i].l,&que[i].r); que[i].id = i; que[i].x = a[que[i].l]; } sort(que,que+m,cmp); priority_queue <Query,vector<Query>,greater<Query> > q; int tp = 0; Query tmp; for(int i = 1; i <= n; ++i) { while(!q.empty() && q.top().x >= a[i]) { tmp = q.top(); q.pop(); if(tmp.r < i) { ans[tmp.id] = tmp.x; continue; } tmp.x %= a[i]; if(tmp.r == i) { ans[tmp.id] = tmp.x; continue; } q.push(tmp); } while(tp < m && que[tp].l == i) { q.push(que[tp]); tp++; } } while(!q.empty()) { tmp = q.top(); q.pop(); ans[tmp.id] = tmp.x; } for(int i = 0; i < m; ++i) printf("%d\n",ans[i]); } return 0;}
1009 Sparse Graph(补图bfs)
题目大意:
给出一个图和一个起点S(1 <= S <= n)
问补图上S到所有点的最短路(除S外)
原图边很少,用set存尚未遍历的点,bfs即可
代码如下:
#include <iostream>#include <cmath>#include <vector>#include <cstdlib>#include <cstdio>#include <climits>#include <ctime>#include <cstring>#include <queue>#include <stack>#include <list>#include <algorithm>#include <map>#include <set>#define LL long long#define Pr pair<int,int>#define fread(ch) freopen(ch,"r",stdin)#define fwrite(ch) freopen(ch,"w",stdout)using namespace std;const int INF = 0x3f3f3f3f;const int msz = 10000;const int mod = 1e9+7;const double eps = 1e-8;const int maxn = 233;set <int> s;set <int>::iterator iter;map <Pr,bool> mp;int n,m;int ans[212345];void bfs(int u){ queue <int> q; q.push(u); for(iter = s.begin(); iter != s.end(); ++iter) if(*iter == u) { s.erase(iter); break; } ans[u] = 0; while(!q.empty()) { u = q.front(); // printf("%d\n",u); q.pop(); for(iter = s.begin(); iter != s.end(); ) { int v = *iter; //printf("%d %d\n",u,v); if(!mp.count(Pr(u,v))) { q.push(v); ans[v] = ans[u]+1; iter = s.erase(iter--); } else iter++; } }}int main(){ int t,u,v; scanf("%d",&t); while(t--) { mp.clear(); s.clear(); scanf("%d%d",&n,&m); for(int i = 1; i <= n; ++i) s.insert(i); while(m--) { scanf("%d%d",&u,&v); mp[Pr(u,v)] = mp[Pr(v,u)] = 1; } scanf("%d",&u); memset(ans,-1,sizeof(ans)); bfs(u); bool f = 0; for(int i = 1; i <= n; ++i) { if(i == u) continue; if(f) putchar(' '); else f = 1; printf("%d",ans[i]); } puts(""); } return 0;}
1010 Weak Pair(树型dp+树状数组+二分)
题目大意:n个点的树,每个点有一个价值v。
找出满足以下条件的点对。
对于点对(u,v) u为v的祖先,
树型dp过程中,每遇到一个点,把它加到树状数组里。每离开一个点,从树状数组里去掉它。
加val[u]前,二分出*val[u] <= k的最大的val
树状数组里找到当前 <= 该val的点的数量
代码如下:
#include <iostream>#include <cmath>#include <vector>#include <cstdlib>#include <cstdio>#include <climits>#include <ctime>#include <cstring>#include <queue>#include <stack>#include <list>#include <algorithm>#include <map>#include <set>#define LL long long#define Pr pair<int,int>#define fread(ch) freopen(ch,"r",stdin)#define fwrite(ch) freopen(ch,"w",stdout)using namespace std;const int INF = 0x3f3f3f3f;const int msz = 10000;const int mod = 1e9+7;const double eps = 1e-8;const int maxn = 112345;using namespace std;struct Edge{ int v,next;};Edge eg[maxn];int head[maxn];int bit[maxn];LL val[maxn];LL to[maxn];int in[maxn];LL k,tp,ans;int id;void add(int u,int v){ eg[tp].v = v; eg[tp].next = head[u]; head[u] = tp++;}int Lowbit(int x){ return x&(-x);}void Add(int x,int ad){ while(x <= id) { bit[x] += ad; x += Lowbit(x); }}int Sum(int x){ int ans = 0; while(x) { ans += bit[x]; x -= Lowbit(x); } return ans;}map <int,int> mp;LL Search(int x){ int l,r; l = 1,r = id; int ans = -1; while(l <= r) { int mid = (l+r)>>1; if(to[x]*to[mid] <= k) { ans = mid; l = mid+1; } else r = mid-1; } if(ans == -1) return 0; return Sum(ans);}void solve(int u,int pre){ int tmp = ans; ans += Search(mp[val[u]]); //printf("%d %lld %lld\n",u,val[u],ans-tmp); Add(mp[val[u]],1); for(int i = head[u]; i != -1; i = eg[i].next) { solve(eg[i].v,u); } Add(mp[val[u]],-1);}int main(){ int n,t,u,v; scanf("%d",&t); while(t--) { scanf("%d%lld",&n,&k); for(int i = 1; i <= n; ++i) { scanf("%lld",&val[i]); to[i] = val[i]; } sort(to+1,to+n+1); id = 0; mp.clear(); for(int i = 1; i <= n; ++i) { if(i == 1 || to[i] != to[i-1]) { id++; to[id] = to[i]; mp[to[id]] = id; } } memset(bit,0,sizeof(bit)); memset(head,-1,sizeof(head)); tp = 0; memset(in,0,sizeof(in)); for(int i = 1; i < n; ++i) { scanf("%d%d",&u,&v); in[v]++; add(u,v); } ans = 0; for(int i = 1; i <= n; ++i) if(!in[i]) { solve(i,i); break; } printf("%lld\n",ans); } return 0;}
- 【题解】 2016 ACM/ICPC Asia Regional Dalian Online (2+5)
- 2016 ACM/ICPC Asia Regional Dalian Online
- 2016 ACM/ICPC Asia Regional Dalian Online
- 2016 ACM/ICPC Asia Regional Dalian Online(2题)
- 2016 ACM/ICPC Asia Regional Dalian Online 大连区域赛部分题解
- hdu 5875 2016 ACM/ICPC Asia Regional Dalian Online 1008
- HDU 5875 Function 2016 ACM/ICPC Asia Regional Dalian Online
- 2016 ACM/ICPC Asia Regional Dalian Online Sparse Graph(BFS)
- 2016 ACM/ICPC Asia Regional Dalian Online Football Games
- 2016 ACM/ICPC Asia Regional Dalian Online Friends and Enemies
- 2016 ACM/ICPC Asia Regional Dalian Online 1008 Function
- HDU 5875 Function 2016 ACM/ICPC Asia Regional Dalian Online
- 2016 ACM/ICPC Asia Regional Dalian Online 1007
- 2016 ACM/ICPC Asia Regional Dalian Online 大连网赛
- 2016 ACM/ICPC Asia Regional Dalian Online分析
- hdu 5875 2016 ACM/ICPC Asia Regional Dalian Online 1008
- 【题解】 2016 ACM/ICPC Asia Regional Qingdao Online (6+5)
- 【题解】 2015 ACM/ICPC Asia Regional Changchun Online (5+2)
- What is Machine Intelligence vs. Machine Learning vs. Deep Learning vs. Artificial Intelligence (AI)
- 深度学习日记1
- web h5页面注意事项
- 算法汇总
- HTTP协议详解
- 【题解】 2016 ACM/ICPC Asia Regional Dalian Online (2+5)
- 使用java实现,随机取4张牌,使用加减乘除得到24的所有可能情况
- 求格雷码的完整程序源代码
- 继承初始化
- java银联支付
- php中的几个字符串操作函数strtoupper strtolower ucwords ucfirst lcfirst函数
- android获取短信验证码并自动填写
- Java泛型与反射机制---学习笔记
- pl/sql导入存储过程