A:
在Alice和Bob面前的是两个骰子,上面分别写了六个数字。
Alice和Bob轮流丢掷骰子,Alice选择第一个骰子,而Bob选择第二个,如果谁投掷出的数更大,谁就可以获胜。
现在给定这两个骰子上的6个数字,你需要回答是Alice获胜几率更大,还是Bob获胜几率更大。(请注意获胜几率相同的情况)
解法:水题,模拟一下。
#include <bits/stdc++.h>
using namespace std;
int a[10], b[10];
int main(){
int T;
scanf("%d", &T);
while(T--){
int ans1=0,ans2=0;
for(int i=1; i<=6; i++) scanf("%d", &a[i]);
for(int i=1; i<=6; i++) scanf("%d", &b[i]);
for(int i=1; i<=6; i++)
for(int j=1; j<=6; j++)
{
if(a[i]>b[j]) ans1++;
if(a[i]<b[j]) ans2++;
}
if(ans1>ans2) puts("Alice");
else if(ans1<ans2) puts("Bob");
else puts("Tie");
}
}
B:
那么问题来了,你最少需要多少钱才能达成自己的目的呢?
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6+7;
int n, m, S[maxn], top, cnt, edgecnt, dfsclk, head[maxn], id[maxn], a[maxn], fa[maxn], FA[maxn], num[maxn], low[maxn], dfn[maxn];
bool vis[maxn];
LL all[maxn], now[maxn];
struct edge{
int to,next;
}E[maxn*2];
void init(){
memset(head,-1,sizeof(head));
edgecnt=0;
}
void add(int u,int v){
E[edgecnt].to=v,E[edgecnt].next=head[u],head[u]=edgecnt++;
}
void Tarjan(int x){
low[x]=dfn[x]=++dfsclk;
S[++top]=x;vis[x]=1;
for(int i=head[x];~i;i=E[i].next){
int v=E[i].to;
if(!dfn[v]){
Tarjan(v);
low[x]=min(low[x],low[v]);
}else if(vis[v]){
low[x]=min(low[x],dfn[v]);
}
}
if(low[x]==dfn[x]){
cnt++;
while(1){
int Now=S[top--];
vis[Now]=0;
id[Now]=cnt;
all[cnt]+=a[Now];
num[cnt]++;
if(Now==x) break;
}
}
}
bool check(LL x){
LL sum=0;
for(int i=1; i<=cnt; i++) now[i]=all[i];
for(int i=1; i<=cnt; i++){
if(now[i]<x*num[i]){
if(FA[i]!=-1){
now[FA[i]]-=x*num[i]-now[i];
}else sum+=x*num[i]-now[i];
}if(sum>m) return false;
}
return true;
}
int main(){
scanf("%d%d",&n,&m);
init();
for(int i=1; i<=n; i++){
scanf("%d", &fa[i]);
if(fa[i]!=-1&&fa[i]!=i){
add(fa[i],i);
}
}
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
for(int i=1;i<=cnt;i++) FA[i]=-1;
for(int i=1;i<=n;i++) if(fa[i]!=-1&&id[fa[i]]!=id[i]) FA[id[i]] = id[fa[i]];
LL L=0,R=1000000000,ans=0;
while(L<=R){
LL mid=L+R>>1;
if(check(mid)) ans=mid,L=mid+1;
else R=mid-1;
}
return 0*printf("%lld\n", ans);
}
C:
平日里写hash的时候,总有某些选手由于脸黑而导致惨遭卡模数,然后一些恶意卡模数的出题人也因此身败名裂。为了防止被卡,我们用一种高级的随机方式来代替原来的线性随机生成,也就是所谓的随机树!
现在有一棵编号为0~n-1的有根树,其中0是树的根。每个节点初始有一个值Ti。现在要求支持一下两种操作:
1. 给出两个正整数u和x,我们将Tu的值乘以x,我们将这种操作称为SEED操作。
2. 给出一个正整数i,询问Si以及它一共有多少个正约数。其中Si表示以i为根的子树所有点的权值的乘积,我们将这种操作称为RAND操作。
容易发现,这样得到的答案还是很随机的。(其实不是)
你需要回答每一次的询问,由于一个数的约数个数可能非常多,这个数也可以非常大,你只需要把答案对1e9+7取模就可以了。
解法:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100010;
const int mod = 1e9+7;
int n, p[6][maxn];
inline int lowbit(int x){return x&(-x);}
void add(int x, int y, int z){
while(y<=n){
p[x][y]+=z;
y+=lowbit(y);
}
}
int query(int x, int y){
int ret = 0;
while(y){
ret += p[x][y];
y-=lowbit(y);
}
return ret;
}
int getsum(int x, int l, int r){
return query(x,r) - query(x, l-1);
}
int a[maxn], L[maxn], R[maxn], dfsclk;
vector <int> G[maxn];
int pri[] = {2, 3, 5, 7, 11, 13};
void dfs(int u, int pre){
L[u] = ++dfsclk;
for(int i=0; i<6; i++){
int num = 0;
while(a[u]%pri[i]==0){
num++;
a[u]/=pri[i];
}
add(i,L[u],num);
}
for(int i=0; i<G[u].size(); i++){
if(G[u][i]==pre) continue;
dfs(G[u][i], u);
}
R[u]=dfsclk;
}
LL qsm(LL a, LL n){
LL ret = 1;
while(n){
if(n&1) ret=(ret*a%mod)%mod;
a=a*a%mod;
n>>=1;
}
return ret;
}
int main(){
scanf("%d",&n);
for(int i=1; i<n; i++){
int u,v;
scanf("%d %d", &u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=0; i<n; i++) scanf("%d", &a[i]);
int q, x, y;
char op[10];
dfs(0, -1);
scanf("%d", &q);
while(q--){
scanf("%s%d",op,&x);
if(op[0]=='S'){
scanf("%d",&y);
for(int i=0; i<6; i++){
int num = 0;
while(y%pri[i]==0){
y/=pri[i];
num++;
}
add(i,L[x],num);
}
}else{
LL a1=1,a2=1;
for(int i=0; i<6; i++){
int sum = getsum(i, L[x], R[x]);
a1 = a1*qsm(pri[i], sum)%mod;
a2 = a2*(sum+1)%mod;
}
printf("%lld %lld\n", a1,a2);
}
}
return 0;
}
D:
珂朵莉给了你一个无向图,每次查询给t个点以及一个常数s,求有多少个图中的点距离给出的那t个点中至少一个距离 <= s
解法:每次把一堆点拿进队列BFS
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5010;
bool vis[maxn][maxn];
struct Edge
{
int to, next;
} E[maxn * maxn];
int head[maxn], edgecnt;
bool mark[maxn];
void init()
{
memset(head, -1, sizeof(head));
edgecnt = 0;
}
void add(int u, int v)
{
E[edgecnt].to = v, E[edgecnt].next = head[u], head[u] = edgecnt++;
}
queue <pair<int, int> >q;
int bfs(int r)
{
int ret = 0;
while (!q.empty())
{
pair <int, int> now = q.front();
q.pop();
int u = now.first, d = now.second;
if (d <= r) ret++;
if (d > r) continue;
for (int i = head[u]; ~i; i = E[i].next)
{
int v = E[i].to;
if (mark[v] == false)
{
q.push(make_pair(v, d + 1));
mark[v] = true;
}
}
}
return ret;
}
int main()
{
int n, m, T;
scanf("%d%d%d", &n, &m, &T);
init();
while (m--)
{
int u, v;
scanf("%d%d", &u, &v);
if (vis[u][v] == false)
{
add(u, v);
add(v, u);
vis[u][v] = vis[v][u] = true;
}
}
int t, s, u;
while (T--)
{
scanf("%d%d", &t, &s);
memset(mark, false, sizeof(mark));
while (t--)
{
scanf("%d", &u);
if (!mark[u])
{
q.push(make_pair(u, 0));
mark[u] = 1;
}
}
int ans = bfs(s);
printf("%d\n", ans);
}
}
E:
珂朵莉给了你一个序列,有n*(n+1)/2个子区间,求出她们各自的逆序对个数,然后加起来输出
解法:
10分做法:puts(“0” );
30分做法:直接暴力
60分做法:优化暴力
70分做法:
考虑计算贡献
如果有i < j , a[i] > a[j]的点对( i , j )
则会在1 <= l <= i , j <= r <= n的子区间[l,r]中出现
则对答案的贡献是i * ( n - j + 1 )
所以用树状数组维护或者分治(归并排序)即可
做法和求普通逆序对差不多
80->100分做法:
发现答案是O( n^4 )级别
冷静分析出题人会不会卡爆long long
发现随机数据答案是O( n^4 )级别
放弃吧,还是写高精度吧
根据常数不等,80分->100分
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1000010;
struct BigInt
{
int a[105];
int len;
BigInt()
{
memset(a, 0, sizeof(a));
len = 1;
}
void add(LL b)
{
int t[105] = {0};
int L = 0;
while (b)
{
t[++L] = b % 10;
b /= 10;
}
len = max(len, L);
for (int i = 1; i <= len; i++) a[i] += t[i];
for (int i = 1; i <= len; i++)
{
a[i + 1] += a[i] / 10;
a[i] %= 10;
}
if (a[len + 1]) len++;
}
void Print()
{
for (int i = len; i >= 1; i--)
{
printf("%d", a[i]);
}
printf("\n");
}
};
int n, cnt = 1, a[maxn];
pair <int, int> t[maxn];
LL c[maxn];
inline int lowbit(int x)
{
return x & -x;
}
void add(int pos, int v)
{
while (pos > 0)
{
c[pos] += v;
pos -= lowbit(pos);
}
}
LL query(int pos)
{
LL ret = 0;
while (pos <= cnt)
{
ret += c[pos];
pos += lowbit(pos);
}
return ret;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
t[i] = make_pair(a[i], i);
}
sort(t + 1, t + n + 1);
for (int i = 1; i <= n; i++)
{
if (t[i - 1].first != t[i].first) cnt++;
a[t[i].second] = cnt;
}
BigInt ans;
for (int i = 1; i <= n; i++)
{
add(a[i], i);
ans.add(query(a[i] + 1) * 1LL * (n - i + 1));
}
ans.Print();
}
F:
有n种大佬,第i种大佬有ai个
珂朵莉想让最少个数的一种大佬的个数最多
你可以创造m个任意种类的大佬,并且可以把一些大佬变成另一些大佬
x -> y意味着可以把任意个x类型的大佬变成y类型的大佬
一个大佬可以被转换多次
对于每个y,最多有一个x使得x -> y成立
解法:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6+7;
int n, m, S[maxn], top, cnt, edgecnt, dfsclk, head[maxn], id[maxn], a[maxn], fa[maxn], FA[maxn], num[maxn], low[maxn], dfn[maxn];
bool vis[maxn];
LL all[maxn], now[maxn];
struct edge{
int to,next;
}E[maxn*2];
void init(){
memset(head,-1,sizeof(head));
edgecnt=0;
}
void add(int u,int v){
E[edgecnt].to=v,E[edgecnt].next=head[u],head[u]=edgecnt++;
}
void Tarjan(int x){
low[x]=dfn[x]=++dfsclk;
S[++top]=x;vis[x]=1;
for(int i=head[x];~i;i=E[i].next){
int v=E[i].to;
if(!dfn[v]){
Tarjan(v);
low[x]=min(low[x],low[v]);
}else if(vis[v]){
low[x]=min(low[x],dfn[v]);
}
}
if(low[x]==dfn[x]){
cnt++;
while(1){
int Now=S[top--];
vis[Now]=0;
id[Now]=cnt;
all[cnt]+=a[Now];
num[cnt]++;
if(Now==x) break;
}
}
}
bool check(LL x){
LL sum=0;
for(int i=1; i<=cnt; i++) now[i]=all[i];
for(int i=1; i<=cnt; i++){
if(now[i]<x*num[i]){
if(FA[i]!=-1){
now[FA[i]]-=x*num[i]-now[i];
}else sum+=x*num[i]-now[i];
}if(sum>m) return false;
}
return true;
}
int main(){
scanf("%d%d",&n,&m);
init();
for(int i=1; i<=n; i++){
scanf("%d", &fa[i]);
if(fa[i]!=-1&&fa[i]!=i){
add(fa[i],i);
}
}
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
for(int i=1;i<=cnt;i++) FA[i]=-1;
for(int i=1;i<=n;i++) if(fa[i]!=-1&&id[fa[i]]!=id[i]) FA[id[i]] = id[fa[i]];
LL L=0,R=1000000000,ans=0;
while(L<=R){
LL mid=L+R>>1;
if(check(mid)) ans=mid,L=mid+1;
else R=mid-1;
}
return 0*printf("%lld\n", ans);
}