【膜你賽】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
#include<cstdio>
#include<iostream>
#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 = 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=_ans*tt%MOD;
tt=tt*tt%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&&i*prime[j]<=x;j++)
{
is_not_prime[i*prime[j]]=true;
pre[i*prime[j]]=j;
if(!(i%prime[j])) break;
}
}
}
T3
Description
\(n\)個城市,每個城市有一匹馬。第\(i\)城市的馬最多走\(E_i\)的距離,它的速度是\(S_i\)。第\(i\)城市到第\(j\)城市直接道路的長度為\(D_{i,j}\)若\(D_{i,j}\)=\(-1\)則代表路不存在。\(Q\)次詢問,第\(k\)次詢問詢問從\(U_k\)出發到\(V_k\)最少需要多少的時間。由於人沒有腿不能走路,所以人必須騎馬,人每到一個城市可以換上那個城市的馬繼續前進。如果在道路中間馬走的距離用光了則會 GG,騎著的馬走到新的城市其能夠走的距離不會回復,每次詢問一定存在至少一組解
Input
第一行兩個整數$N$,$Q$
接下來每行兩個整數表示$E_i$,$S_i$。
接下來$N$行每行$N$個整數表示$D_{i,j}$
接下來每行兩個整數表示$U_k$,$V_k$。
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
第一行兩個整數$N$,$Q$
接下來每行兩個整數表示$E_i$,$S_i$。
接下來$N$行每行$N$個整數表示$D_{i,j}$
接下來每行兩個整數表示$U_k$,$V_k$。
Solution
顯然用同一匹馬從\(A\)城市走到\(B\)城市走最短路是最優的。
看到數據範圍以及多組詢問果斷Floyd。
先一遍Floyd處理處兩座城市之間的最短路徑
然後再建立一張圖,枚舉所有的點,如果\(A\)的馬能到\(B\)就連一條邊。
再跑一遍Floyd。完事。
Code
#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
#include<cstdio>
#include<algorithm>
#include<iostream>
#define rg register
#define ci const int
#define cl const long long int
typedef long long int ll;
using namespace std;
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 = 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掃描數組
【膜你賽】2018-9-7