【膜你赛】2018-9-7
是长者的题目。其实还是有点水的。
下面是黑历史
准队爷dzy第一次提交四道题182分。
然后dzy告诉我他那次是提交184分
184就184吧,反正和182没啥区别
机房里有个只提交了两道题的神仙200分
我第一次评测275分
黑历史结束
当然最后准队爷dzy 在多次修改后 AK了
调整后我最终评测300分
一共四道题,T1签到,T2水数学,T3水图论,T4毒瘤数据结构
哎我的DP去哪了
下面是题解
T1
Description
现在有一种加密算法,这种算法是要求你用一个$k$位的数对一个字符串进行加密。加密的算法是我们将这个字符串写下来,然后将这个的数不断反复的写下,然后将对应位相加得到我们的加密串。举个栗子,假设我们用$123$对$abcdz$进行加密的话,我们首先将它们按照上述的方法写下来: $$abcdz$$ $$12312$$ 那么我们将对应位加起来,比如$a + 1 = b$, $z + 2 = b$,就可以得到加密之后 的串为: $$bdfeb$$ 给定字符串和这个数, 求加密后的串。
### Input 第一行两个整数$l,k$ 代表字符串的长度和数的位数。第二行一个长度为\(l\)字符串,代表需要被加密的字符串。
第三行一个有\(k\)位的数,代表加密所需要用到的数。
Output
一行一个字符串,代表加密后的结果
Sample Input
5 3
abcdz
123
Sample Output
bdfeb
Hint
对于100%的数据, \(1~\leq~100,1~\leq~8\),字符串中只会有小写字母,
加密用到的数中只会出现\(0 − 9\)中的字符。
Solution
签到题,直接切
Code
#define rg register
#define ci const int
#define cl const long long int
typedef long long int ll;
namespace IO {
char buf[90];
}
template<typename T>
inline void qr(T &x) {
char ch=getchar(),lst=' ';
while(ch>'9'||ch<'0') lst=ch,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(lst=='-') x=-x;
}
template<typename T>
inline void write(T x,const char aft,const bool pt) {
if(x<0) x=-x,putchar('-');
int top=0;
do {
IO::buf[++top]=x%10+'0';
x/=10;
} while(x);
while(top) putchar(IO::buf[top--]);
if(pt) putchar(aft);
}
template<typename T>
inline T mmax(const T a,const T b) {if(a>b) return a;return b;}
template<typename T>
inline T mmin(const T a,const T b) {if(a<b) return a;return b;}
template<typename T>
inline T mabs(const T a) {if(a<0) return -a;return a;}
template<typename T>
inline void mswap(T &a,T &b) {
T temp=a;a=b;b=temp;
}
const int maxn = 110;
int k,l;
char a[maxn],b[maxn];
int A[maxn],B[maxn];
int main() {
freopen("yi.in","r",stdin);
freopen("yi.out","w",stdout);
qr(l);qr(k);
scanf("%s",a+1);
scanf("%s",b+1);
rg int j=1;
for(rg int i=1;i<=l;++i) A[i]=a[i]-'a';
for(rg int i=1;i<=k;++i) B[i]=b[i]-'0';
for(rg int i=1;i<=l;++i) {
A[i]=(A[i]+B[j++])%26;
if(j>k) j=1;
putchar('a'+A[i]);
}
putchar('\n');
return 0;
}
T2
Description
我们都知道,判断一个数是不是完全平方数实在是太难了,所以为了简化问
题,我们希望从\(1\) − \(n\)中找一些数,使得这些数乘起来是一个完全平方数。但是
求这些数是哪些数还是太难了,所以我们求这个完全平方数最大可能是多少。
Input
一行一个数字\(n\)
Output
一行一个整数代表答案对\(10^8+7\)取模的答案
Sample Input
7
Sample Output
144
Hint
对于20%的数据, $1~\leq~n~\leq~100$。
对于50%的数据, $1~leq~n~\leq~5000$。
对于70%的数据, $1~\leq~n~\leq~10^5$。
对于100%的数据, $1~\leq~n~\leq~5~\times~10^6$。
### Solution 这大概是个数学题叭…… 考虑一个完全平方数,对他进行质因数分解,那么他的唯一分解式任意一项的指数一定是一个偶数。这个命题的逆命题也成立。所以这是一个数字是一个完全平方数的**充要条件**是他的唯一分解式任意一项的指数为偶数。 ##### 证明: 设完全平方数$n=a^2$。其中$a$的唯一分解式为$a=p_1^{a_1}p_2^{a_2}…p_k^{a_k}$。那么$n=a^2=p_1^{2a_1}p_2^{2a_2}…p_k^{2a_k}$。由于等号两边可以互换,所以逆推同样成立。证毕。 考虑对$1$~$n$每一个数进行质因数分解,记录他们每个质数作为因数的个数。选取所有个数大于1的质数,如果它的个数是奇数则$-1$,然后做快速幂,这样就可以得到最大的完全平方数 ### Code ```cpp #includetypedef long long int ll;
namespace IO {
char buf[90];
}
template
inline void qr(T &x) {
char ch=getchar(),lst=' ';
while(ch>'9'||ch<'0') lst=ch,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(lst=='-') x=-x;
}
template
inline void write(T x,const char aft,const bool pt) {
if(x<0) x=-x,putchar('-');
int top=0;
do {
IO::buf[++top]=x%10+'0';
x/=10;
} while(x);
while(top) putchar(IO::buf[top--]);
if(pt) putchar(aft);
}
template
inline T mmax(const T a,const T b) {if(a>b) return a;return b;}
template
inline T mmin(const T a,const T b) {if(a<b) return a;return b;}
template
inline T mabs(const T a) {if(a<0) return -a;return a;}
template
inline void mswap(T &a,T &b) {
T temp=a;a=b;b=temp;
}
const int maxn = 5000010;
const int MOD = 1e8+7;
int n,cnt;
int prime[maxn],pre[maxn];
ll ans=1;
ll MU[maxn];
bool is_not_prime[maxn];
void Get_Prime(ci);
int main() {
freopen("two.in","r",stdin);
freopen("two.out","w",stdout);
qr(n);
Get_Prime(n);
for(rg int i=2;i<=n;++i) {
int _temp=i;
while(_temp != 1) {
++MU[pre[_temp]];
_temp/=prime[pre[_temp]];
}
}
for(rg int i=1;i<=cnt;++i) if(MU[i]) {
MU[i]=(MU[i]|1)^1;if(!MU[i]) continue;
ll _ans=1,tt=prime[i];
while(MU[i]) {
if(MU[i]&1) _ans=_anstt%MOD;
tt=tttt%MOD;
MU[i]>>=1;
}
ans=ans*_ans%MOD;
}
write(ans,'\n',true);
return 0;
}
void Get_Prime(int x) {
for(rg int i=2;i<=x;i++) {
if(!is_not_prime[i]){prime[++cnt]=i;pre[i]=cnt;}
for(rg int j=1;j<=cnt&&iprime[j]<=x;j++)
{
is_not_prime[iprime[j]]=true;
pre[i*prime[j]]=j;
if(!(i%prime[j])) break;
}
}
}
[T3](https://www.luogu.org/problemnew/show/U38871)
### Description
$n$个城市,每个城市有一匹马。第$i$城市的马最多走$E_i$的距离,它的速度是$S_i$。第$i$城市到第$j$城市直接道路的长度为$D_{i,j}$若$D_{i,j}$=$-1$则代表路不存在。$Q$次询问,第$k$次询问询问从$U_k$出发到$V_k$最少需要多少的时间。由于人没有腿不能走路,所以人必须骑马,人每到一个城市可以换上那个城市的马继续前进。如果在道路中间马走的距离用光了则会 GG,骑着的马走到新的城市其能够走的距离不会回复,每次询问一定存在至少一组解
### Input
<p><p>第一行两个整数$N$,$Q$ </p>
<p>接下来每行两个整数表示$E_i$,$S_i$。</p>
<p>接下来$N$行每行$N$个整数表示$D_{i,j}$</p>
<p>接下来每行两个整数表示$U_k$,$V_k$。</p></p>
### Output
输出共$Q$行每行一个保留六位的实数表示答案
### Sample Input_1
3 1
2 3
2 4
4 4
-1 1 -1
-1 -1 1
-1 -1 -1
1 3
### Sample Output
0.583333
### Sample Input_2
4 3
30 60
10 1000
12 5
20 1
-1 10 -1 31
10 -1 10 -1
-1 -1 -1 10
15 6 -1 -1
2 4
3 1
3 2
### Sample Output_2
0.510000
8.010000
8.000000
### Hint
<p><p>第一行两个整数$N$,$Q$ </p>
<p>接下来每行两个整数表示$E_i$,$S_i$。</p>
<p>接下来$N$行每行$N$个整数表示$D_{i,j}$</p>
<p>接下来每行两个整数表示$U_k$,$V_k$。</p></p>
### Solution
显然用同一匹马从$A$城市走到$B$城市走最短路是最优的。
看到数据范围以及多组询问果断Floyd。
先一遍Floyd处理处两座城市之间的最短路径
然后再建立一张图,枚举所有的点,如果$A$的马能到$B$就连一条边。
再跑一遍Floyd。完事。
### Code
```cpp
#include<cstdio>
#define rg register
#define ci const int
#define cl const long long int
typedef long long int ll;
namespace IO {
char buf[90];
}
template<typename T>
inline void qr(T &x) {
char ch=getchar(),lst=' ';
while(ch>'9'||ch<'0') lst=ch,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(lst=='-') x=-x;
}
template<typename T>
inline void write(T x,const char aft,const bool pt) {
if(x<0) x=-x,putchar('-');
int top=0;
do {
IO::buf[++top]=x%10+'0';
x/=10;
} while(x);
while(top) putchar(IO::buf[top--]);
if(pt) putchar(aft);
}
template<typename T>
inline T mmax(const T a,const T b) {if(a>b) return a;return b;}
template<typename T>
inline T mmin(const T a,const T b) {if(a<b) return a;return b;}
template<typename T>
inline T mabs(const T a) {if(a<0) return -a;return a;}
template<typename T>
inline void mswap(T &a,T &b) {
T temp=a;a=b;b=temp;
}
const int maxn = 110;
const double INF = 1e18;
int n,q,a,b;
double e[maxn],s[maxn],frog[maxn][maxn],MU[maxn][maxn];
int main() {
freopen("iii.in","r",stdin);
freopen("iii.out","w",stdout);
qr(n);qr(q);
for(rg int i=1;i<=n;++i) scanf("%lf%lf",&e[i],&s[i]);
for(rg int i=1;i<=n;++i) for(rg int j=1;j<=n;++j) {scanf("%lf",&frog[i][j]);if(frog[i][j] == -1.0) frog[i][j] =1e18;MU[i][j]=1e18;}
for(rg int k=1;k<=n;++k) for(rg int i=1;i<=n;++i) for(rg int j=1;j<=n;++j) frog[i][j]=mmin(frog[i][j],frog[i][k]+frog[k][j]);
for(rg int i=1;i<=n;++i) for(rg int j=1;j<=n;++j) if(frog[i][j] <= e[i]) MU[i][j]=mmin(MU[i][j],frog[i][j]/s[i]);
for(rg int k=1;k<=n;++k) for(rg int i=1;i<=n;++i) for(rg int j=1;j<=n;++j) MU[i][j]=mmin(MU[i][j],MU[i][k]+MU[k][j]);
while(q--) {
a=b=0;qr(a);qr(b);
printf("%.6lf\n",MU[a][b]);
}
return 0;
}
T4
Description
给你 n 个数$ a_1, a_2,⋯, a_n$,我们希望知道有多少个(l, r)满足$1~\leq~l~<~r~\leq~n$, 且$a_1, a_2,⋯, a_l, a_r,⋯, a_n$的逆序对个数不超过k。
Input
第一行两个整数\(n, k\)。
接下来一行\(n\)个整数代表序列。
Output
一行一个整数代表答案。
Sample Input_1
3 1
1 3 2
Sample Output_1
3
Sample Input_2
5 2
1 3 2 1 7
Sample Output_2
6
Hint
对于30%的数据,$1~\leq~n~\leq~10^3$。
对于另外20%的数据,$k = 0$。
对于100%的数据,$1~\leq~n~\leq~10^5, 1~\leq~a_i~\leq~10^9, n~\leq~10^{18}$。
### Solution 看到数据范围,考虑只能$O(n)$扫一遍了。 考虑对于挖去区间(l,r)的逆序对个数,如果r+1,那么个数一定单调不升。l+1同理,个数一定单调不降。所以考虑双指针。 使用两个树状数组维护[1,l]和[r,n]的数的个数,然后双指针扫描数组。r+1时逆序对个数减去r贡献的逆序对,l+1时逆序对个数加上l贡献的逆序对个数,找到最靠左的r。然后累加答案即可。 ### Code ```cpp #includetypedef long long int ll;
using namespace std;
namespace IO {
char buf[90];
}
template
inline void qr(T &x) {
char ch=getchar(),lst=' ';
while(ch>'9'||ch<'0') lst=ch,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(lst=='-') x=-x;
}
template
inline void write(T x,const char aft,const bool pt) {
if(x<0) x=-x,putchar('-');
int top=0;
do {
IO::buf[++top]=x%10+'0';
x/=10;
} while(x);
while(top) putchar(IO::buf[top--]);
if(pt) putchar(aft);
}
template
inline T mmax(const T a,const T b) {if(a>b) return a;return b;}
template
inline T mmin(const T a,const T b) {if(a<b) return a;return b;}
template
inline T mabs(const T a) {if(a<0) return -a;return a;}
template
inline void mswap(T &a,T &b) {
T temp=a;a=b;b=temp;
}
const int maxn = 100010;
int n,upceil;
ll k,cnt,ans;
int MU[maxn],temp[maxn];
ll front[maxn],back[maxn];
inline int lowbit(ci x) {return x&((~x)+1);}
void add(ll*,int);
ll asklower(int);
ll askupper(int);
void dlt(int);
void dctz() {
std::sort(temp+1,temp+1+n);
upceil=std::unique(temp+1,temp+1+n)-temp-1;
for(rg int i=1;i<=n;++i) MU[i]=std::lower_bound(temp+1,temp+1+upceil,MU[i])-temp;
}
int main() {
qr(n);qr(k);
for(rg int i=1;i<=n;++i) {qr(MU[i]);temp[i]=MU[i];}
dctz();
int l=1,r=2;
for(rg int i=n;i>=r;--i) {
cnt+=asklower(MU[i]-1);
add(back,MU[i]);
}
add(front,MU[1]);cnt+=asklower(MU[1]-1);
while(r <= n) {
if((cnt <= k) && (l < r)) {
ans+=n-r+1;
++l;cnt+=asklower(MU[l]-1) + (l-1-askupper(MU[l]));
add(front,MU[l]);
}
else {
cnt-=(l-askupper(MU[r])) + asklower(MU[r]-1);
dlt(MU[r]);
++r;
}
}
write(ans,'\n',true);
return 0;
}
void add(ll*t,int x) {
while(x <= upceil) {
++t[x];
x+=lowbit(x);
}
}
ll asklower(int x) {
ll _ans=0;
while(x) {
_ans+=back[x];
x-=lowbit(x);
}
return _ans;
}
ll askupper(int x) {
ll _ans=0;
while(x) {
_ans+=front[x];
x-=lowbit(x);
}
return _ans;
}
void dlt(int x) {
while(x<=upceil) {
--back[x];
x+=lowbit(x);
}
}
### Summary
T1:签到题数据好造的话一定要写两遍正解对拍。拍拍更健康(逃
#### ~~另外签到题不要像准队爷dzy一样把输出文件写成yi.in~~
T2:
$O(logn)$的分解质因数方法:
筛出质数,同时记录每个数的最小素因子然后让被分解的数字被他的最小素因子除掉,把最小素因子的个数++,然后再把得数除掉得数的最小素因子。如此迭代直到得数是1.
T3
看到多组数据其实Floyd是个不错的办法。
INF一定要开的足够大
对于无需考虑精度误差的题目,如果后面计算无法避免使用浮点数,可以从一开始就考虑读入浮点数进行处理从而减小误差
T4
当题目满足区间端点移动时有单调性时,考虑two points扫描数组