赛时只出了两题 09 10
06卡死了。很烂,没有及时弃疗06,最后得不偿失
丢人。
1001 Different Circle Permutation(矩快+polya+欧拉函数)
题目大意:n个座位围成一圈,n个人中挑出若干个人坐下,要求相邻座椅不可同时有人。n个座位均匀分布,即相邻座位与圆心夹角为 2πN 。问共有多少种方案,旋转相同算一种。
问题可转化为环上n个点涂色,黑白两色,要求相邻点不可同时为黑。
若不考虑相邻不可同为黑,就是经典的染色问题,旋转相同为同构,对称相同不算同构。
polya定理可解,为1/|G|*(mC(π1)+mC(π2)+mC(π3)+…+mC(πk)).
由于n很大,所以用欧拉函数求gcd。
即 f(n)=1n∑ni=12ngcd(i,n)=1n∑d|n2nφ(nd)d
此上是考虑黑白随意涂色。
题目限制黑色不可相邻。其实对于给定的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个点的所有情况都考虑到了。
这样带入之前公式。即为
f(n)=1n∑ni=1f(gcd(i,n))=1n∑d|nφ(nd)f(d)
枚举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这种情况。
那么
∃(1≤k≤n)∑ki=1s[i]>=i(i−1)
&&
∑ni=1s[i]==n(n−1)
则分数无误。
即对于此题 得分最少的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] 输出
a[l]moda[l+1]moda[l+2]...moda[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的祖先,
valu∗valv<=k
树型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;
}

本文总结了多项算法竞赛题目,包括不同圈排列问题、子数组最大公约数查询、足球比赛得分合法性验证、二分图完美匹配等,提供了详细的解题思路与代码实现。
1186

被折叠的 条评论
为什么被折叠?



