我又一次被虐爆了,T4竟暴力可以过(我所认为的暴力)!
这一次只有140分,Rank20。
T1:
Coprime
题目大意
对于两个整数k 和m,如果k 和m 的最大公约数为1,则k 和m 互质。给出两个正整
数n 和m(m≤n),定义f(n,m)为1~n!中与m!互质的数的个数。其中n!=123*…*(n-1)n。
Task:给定n 和m,要求计算f(n,m)。
输入
本题设多组数据。
输入文件的第一行有一个整数T(1≤T≤100000),表示有T 组数据。
接下来有T 行,每行两个整数n 和m(2≤n≤100000,2≤m≤n)。
输出
输出文件包含T 行,每行一个整数,表示f(n,m)。
由于答案过大,所以你只要输出f(n,m) mod 131071。
131071 是M17(梅森素数,2^17-1)。
样例输入
1
3 2
样例输出
3
数据范围
对于50%的数据,T=1,2≤N≤10
对于80%的数据,T=1,2≤N≤100000
对于100%的数据,1≤T≤100000,2≤N≤100000
AcFast 友情提示:小心运算溢出,也就是RTE215 错误。。。
题解
对于一个质数p,用n!(p-1)/p,最后就是答案,用逆元(费马小定理、欧拉函数)求出答案。(其实是道板子题)
#include
#define RE register
#define ll long long
using namespace std;
const int P=131071;
int prime[100001],num=0,T;
ll fact[100001],ans,phi[100001];
bool bz[100001];
inline int read() {
int s=0;
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
return s;
}
inline ll power(ll a,ll b) {
ll result=1;
while(b>0) {
if(b&1)result=result*a%P;
a=a*a%P;
b>>=1;
}
return result;
}
inline void init() {
fact[1]=1;
for(RE int i=2; i<=100000; i++)fact[i]=fact[i-1]*i%P;
phi[1]=1;
for(RE int i=2; i<=100000; i++) {
phi[i]=phi[i-1];
if(!bz[i]) {
phi[i]=phi[i]*(i-1)%P*power(i,P-2)%P;
prime[++num]=i;
}
for(RE int j=1; j<=num; j++) {
if(prime[j]*i>100000)break;
bz[prime[j]*i]=1;
if(i%prime[j]==0)break;
}
}
}
signed int main() {
freopen("coprime.in","r",stdin);
freopen("coprime.out","w",stdout);
init();
T=read();
while(T--) {
int n=read(),m=read();
printf("%lld\n",phi[m]*fact[n]%P);
}
}
T2:
Matrix
题目大意
给你一个N*M 的矩阵,矩阵里面的元素要么是正数,要么是负数,它们的绝对值不大
于10000。现在你可以对矩阵进行两种操作:
1、将某一列的元素全部取反。
2、将某一行的元素全部取反。
你可以执行任意次操作。
Task:通过以上两种操作如果可以将整个矩阵的元素全变为正数的话,则输出最少的操
作次数,否则输出“impossible”(不包括双引号)。
输入
输入文件的第一行有两个整数n 和m(1≤n,m≤1000),表示矩阵的大小。
接下来有N 行,每行M 个数,每个数之间有一个空格。
输出
通过以上两种操作如果可以将整个矩阵的元素全变为正数的话,则输出最少的操作次
数,否则输出“impossible”(不包括双引号)。
样例输入
2 4
3 7 -6 1
-2 -6 8 -4
样例输出
2
数据范围
对于40%的数据,1≤N,M≤10
对于100%的数据,2≤N,M≤1000
题解
很明显,给出的那几个数只需要判断正负,所以我们先把它转化为01矩阵,正数为0,负数为1
那么样例就变成了这个样子
1 1 0 1
0 0 1 0
不难发现,先操作哪一行哪一列的先后顺序并不影响最终的状态,只要操作的数量相同,操作的行列相同就可以了。所以我们现在就有一个思路,我们可以先尝试把每一行都转化为相同状态,然后再把每一列进行处理,就可以得出答案。那么,每一行,它至多有两种状态,一种是操作,另一种是不操作,这样我们就可以以第一行为基准,看一下之后的每一行是否能够变成第一行的一二种状态,然后再求最小值。
Code
#include
using namespace std;
int n,m,s[1001][1001],s2[1001][1001],ans=0,ans2=1,sum=0x3f3f3f3f;
bool bz=0,bz2=0;
inline int min(int a,int b) {
if(a<b)return a;
return b;
}
int main() {
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) {
int x;
scanf("%d",&x);
if(x>0)s[i][j]=0;
else s[i][j]=1;
s2[i][j]=(s[i][j]+1)&1;
}
for(int i=2;i<=n;i++) {
bool check=0,check2=0,bz3=0,bz4=0;
for(int j=1;j<=m;j++) {
if(s[i][j]!=s[1][j])check=1;
if(s[i][j]!=s2[1][j])check2=1;
if(s2[i][j]!=s[1][j])bz3=1;
if(s2[i][j]!=s2[1][j])bz4=1;
if(check&&check2&&bz3&&bz4)break;
}
if(check&&check2)bz=1;
if(bz3&&bz4)bz2=1;
if(bz&&bz2)break;
if(!bz3)ans++;
if(!bz4)ans2++;
}
if(!bz) {
for(int j=1;j<=m;j++)
ans+=s[1][j];
sum=ans;
}
if(!bz2) {
for(int j=1;j<=m;j++)
ans2+=s2[1][j];
sum=min(sum,ans2);
}
if(bz&&bz2)puts("impossible");
else printf("%d",sum);
}
T3:
题目大意
Hanoi
Mpq 小时候只玩过俄罗斯方块这个经典的小游戏,当时他还不知道Hanoi 究竟是
什么东西。话说当Mpq 第一次认识Hanoi 是在初三那年的联赛。由于Mpq 之前并不知
道Hanoi 是什么东西,所以那一年他做完前三题之后很郁闷地坐了1 个半小时。。。
好了,现在Mpq 成长了,他已经解决当年联赛那道Hanoi 了,在前几个月,他又
发现一道关于Hanoi 的题目了,很幸运的是这个题目他知道怎么做了。。。然后为了让大
家体验一下Mpq 初三联赛那种无奈的感觉,所以,这道题就神奇地出现在你们眼前。
Task:赶快AC 这道题目,然后你就可以狂鄙视,甚至是无视Mpq 的存在了!!!
哎,吹着吹着发现我还没把题目写下来。。。。
现在给你M 根柱子,初始的时候有N 个大小不一样的盘插在第一根柱子上面。同
样地,规格大的盘子不能放在规格比它小的盘子上面。问最少需要多少次的移动才能将
这N 个盘从第一根柱子移动到最后一根柱子上面?
输入
输入文件的第一行有两个整数n,m(1≤n≤100000,3≤m≤10),分别表示有n 个盘子和m
根柱子。
输出
输出文件只有一行,一个整数,表示最少的移动次数。保证这个移动次数不会超过
2^63-1。
样例输入
4 3
样例输出
15
数据范围
对于30%的数据,M=3
对于80%的数据,1≤N≤100,3≤M≤10
对于100%的数据,1≤N≤100000,6≤M≤10
题解
看到这道题时,知道m才这么小,一下就想到了状压,一股脑往状压去想了,结果想对了一半,是dp,但不是状压。说这么多无聊的,应该开始讲正解了。
设一个状态f[i][j]表示i个盘子,j根柱子的最小步数。
可是还是想不到怎么转移,那就先从最简单的三根柱子开始。
i个盘子三根柱子的答案套用公式:
f[i][3]=f[i-1][3]*2+1;
那么对于当前的一个k,代表从第一个柱子搬k个下来,然后再把他们给堆在一起放在最后一个柱子,那么
f[i][j]=f[k][j]*2+f[i-k][j-1];
这么一下来,答案是对了,但是超时了。
所以我们就用一个g[i][j]代表一个最优的k,每一次k就等于g[i-1][j],和g[i-1][j]+1,每次就从上面选最优转移。
Code
#include
using namespace std;
int n,m;
long long f[100001][11],g[100001][11];
int main() {
freopen("hanoi.in","r",stdin);
freopen("hanoi.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=63;i++) {
f[i][3]=(f[i-1][3]*2)|1;
g[i][3]=i-1;
}
for(int i=1;i<=n;i++)
for(int j=4;j<=m;j++) {
int k=g[i-1][j];
f[i][j]=(f[k][j]<<1)+f[i-k][j-1];
g[i][j]=k;
k++;
if((f[k][j]<<1)+f[i-k][j-1]<f[i][j]) {
f[i][j]=(f[k][j]<<1)+f[i-k][j-1];
g[i][j]=k;
}
}
printf("%lld",f[n][m]);
}
T4
题目大意
Road
Z 国是一个拥有N 个岛的国家。这N 个岛用N-1 条桥来连接,且任意两个岛之间都可以互达。
某商人听说Z 国是一个很富有的国家,所以他想到Z 国闯一闯。经过他仔细的观察,他发现某样商品特别受欢迎,而且由于各岛之间沟通联系不够多,所以这样物品在每个岛的价格可能都不同。
Task:商人开始在编号为x 的岛上,然后他要走到编号为y 的岛上。在这期间,他可以在x 岛y 的路径上买一件商品,和卖一件商品。,注意,仅能买一件和卖一件!显然你要计算商人从岛x 到岛y 最多能赚多少钱。
输入
输入第一行有一个整数N(1≤n≤50000),表示Z 国有N 个岛。
接下来有N 行,每行一个整数Ci(1≤Ci≤50000),第N+i 行的Ci 表示商品在岛i 的价
格。
再接下来有N-1 行,每行两个整数x,y(1≤x,y≤50000),表示岛x 和岛y 之间有一条
桥。
接下来有一个整数M,表示有M 个询问。
然后M 行,每行两个整数,x,y(1≤x,y≤50000),表示询问你,商人从岛x 到岛y 最多
能赚多少钱?
输出
对于每次询问,如果商人能赚到钱,则输出最多能赚多少钱。
如果不能赚钱,就输出0(你可以这样理解——亏本生意谁都不会做_)
样例输入
4
1
2
3
4
1 2
1 4
2 3
3
1 3
3 1
1 4
样例输出
2
0
3
数据范围
数据约定:
对于30%的数据, 1≤N,M≤100
对于60%的数据,1≤N,M≤1000
对于100%的数据,1≤N,M≤50000
题解
好像梁正昊给我们讲过这题
这道题令我特别的愤怒,竟是一道暴力!本O(nm)所不能过的题让某些人水了过去,就连LCA直接打一个个往上跳的打暴力都能过。严厉谴责出数据人!
就是一个暴力,找到LCA,(劝各位打高级的LCA算法)然后把y到LCA这条路径上的点加入数列,然后x到lca的路径找答案,再逆向在数列里找答案,一道暴力就AC了。
Code
#include
#include
#include
#include
using namespace std;
int n,a[50001],head[50001],cnt=0,st[50001][21],dep[50001],m,maxn,minn,minn2,ans,b[50001],num;
struct node {
int to,next;
} edge[100000];
void add(int x,int y) {
edge[cnt].to=y;
edge[cnt].next=head[x];
head[x]=cnt++;
}
void dfs(int now) {
for(int i=head[now]; i!=-1; i=edge[i].next) {
int son=edge[i].to;
if(son==st[now][0])continue;
st[son][0]=now;
dep[son]=dep[now]+1;
dfs(son);
}
}
int LCA(int x,int y) {
if(dep[x]<dep[y])swap(x,y);
if(dep[x]>dep[y]) {
for(int j=log2(dep[x]-dep[y]); j>=0; j--)
if(dep[st[x][j]]>dep[y])x=st[x][j];
x=st[x][0];
if(x==y)return x;
}
for(int j=log2(dep[x]); j>=0; j--)
if(st[x][j]!=st[y][j])
x=st[x][j],y=st[y][j];
return st[x][0];
}
int main() {
/*freopen("road.in","r",stdin);
freopen("road.out","w",stdout);*/
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
memset(head,-1,sizeof(head));
for(int i=1; i<n; i++) {
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
st[1][0]=1;
dfs(1);
for(int j=1; j<=log2(n); j++)
for(int i=1; i<=n; i++)
st[i][j]=st[st[i][j-1]][j-1];
scanf("%d",&m);
while(m--) {
int u,v;
scanf("%d%d",&u,&v);
int lca=LCA(u,v);
ans=0;
minn=0x3f3f3f3f;
num=0;
maxn=0;
while(u!=lca) {
ans=max(ans,a[u]-minn);
minn=min(minn,a[u]);
u=st[u][0];
}
ans=max(ans,a[u]-minn);
minn=min(minn,a[u]);
while(v!=lca) {
b[++num]=v;
v=st[v][0];
}
b[++num]=v;
minn2=minn;
for(int i=num; i>=1; i--) {
maxn=max(maxn,a[b[i]]);
ans=max(ans,a[b[i]]-minn);
minn=min(minn,a[b[i]]);
}
ans=max(ans,maxn-minn2);
printf("%d\n",ans);
}
}
请各位大佬多多知道我这个蒟蒻。