大假期集訓模擬賽11
解方程
題目描述
解出一元二次方程 \(ax+by=c\) 的一組整數解 \((x0, y0)\) ,使 \(\left |x_0+y_0 \right |\) 最小。
輸入格式
共一行,三個整數 \(a\) , \(b\) , \(c\) 。
輸出格式
共一行,為 \(\left |x_0+y_0 \right |\) 的最小值。
若無解輸出 \(“kito”\) 。
樣例
樣例輸入 #1
1 1 1
樣例輸出 #1
1
樣例輸入 #2
2 3 1
樣例輸出 #2
0
資料範圍與提示
有 \(30\%\) 的資料,\(a,b\) 均為質數,\(c=1\) 。
另有 \(20\%\) 的資料,\(a,b,c\)
\(100\%\) 的資料,\(a,b,c\leq 1,000,000,000\) 。
思路
當時考試以為是個大數論,直接跳過,拿了特判 \(10opts\) 走人。
其實思路很簡單,\(x,y\) 只能是整數,推一下式子就行了。
設\(\left |x_0+y_0 \right |=i\)
\(\therefore x=i-y\) 或 \(x=-i-y\)
\(\therefore y=\frac {c\pm a*i} {b-a}\)
這樣只需列舉 \(i\) ,並判斷求出來的 \(y\) 是不是整數即可。
注意要特判一下 \(b==a\) ,不然會出錯。
程式碼
#include <bits/stdc++.h> #define int long long using namespace std; const int maxn=1e9+50; inline int read(){ int x=0,w=1; char ch; for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1; for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; return x*w; } int a,b,c; signed main(){ a=read(),b=read(),c=read(); if(c==0){//加這個會更快一丟丟 printf("0\n"); return 0; } if(a==b){//特判a==b if(c%a==0){ printf("%lld\n",c/a); }else{ printf("kito\n"); } return 0; } for(int i=0;i<=maxn;i++){//列舉i if((c-a*i)%(b-a)==0){ printf("%lld\n",i); return 0; } if((c+a*i)%(b-a)==0){ printf("%lld\n",i); return 0; } } printf("kito\n"); return 0; }
最佳序列
題目描述
你得到了一個 \(N\) 個非負整陣列成的序列 \(A\) 。
我們稱由序列 \(A\) 的連續若干項組成的新序列為 \(A\) 的一個連續子序列。給出兩個正整數 \(L,R(L\leq R)\) 。稱 \(A\) 的每一個長度不小於 \(L\) 且不大於 \(R\) 的連續子序列為一個純潔序列,定義純潔度為純潔序列的所有元素的平均值。
請你求出所有純潔序列中的純潔度的最大值。
輸入格式
輸入檔案的第一行包括 \(3\) 個正整數 \(N,L,R\) 。
第二行包括 \(N\) 個數,按順序依次表示序列 \(A\) 的每一項。
輸出格式
輸出檔案包括一行,一個實數,保留 \(4\)
樣例
樣例輸入
3 2 3
6 2 8
樣例輸出
5.3333
資料範圍與提示
\(20\%\) 的資料滿足,\(1\leq N\leq 200\) 。
\(40\%\) 的資料滿足,\(1\leq N\leq 2,000\) 。
\(100\%\) 的資料滿足,\(1\leq N\leq 20,000\) , \(1\leq L\leq R\leq N\) , \(0\leq A_i\leq 1,000,000\) 。
思路
當時考試的時候想出了 \(3\) 種方法:
-
\(n^3\)的暴力,\(20,000\) 肯定會爆。
-
在上面的基礎上用字首和優化一下,直接壓到 \(n^2\) 。
-
用單調佇列來維護,二分查詢答案,直接壓倒 \(nlog_n\),
但是我考試的時候寫炸了。
程式碼
法二暴力
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e4+50;
inline int read(){
int x=0,w=1;
char ch;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
int n,l,r;
int a[maxn],sum[maxn];
double ans;
int main(){
n=read(),l=read(),r=read();
for(register int i=1;i<=n;i++){
a[i]=read();
sum[i]=sum[i-1]+a[i];
}
for(register int d=l;d<=r;d++){//register優化一下
int now=0;
for(register int i=1,j;(j=i+d-1)<=n;i++){
now=max(now,sum[j]-sum[i-1]);//除法會比較慢,所以求出和的最大,再進行除法,會很快
}
ans=max(ans,(double)now/d);
}
printf("%.4lf\n",ans);
return 0;
}
法三(借用一手大佬的碼)
#include <bits/stdc++.h>
using namespace std;
const int maxn=20000+10;
int l,r,n;
int a[maxn];
double tmp[maxn];
double sum[maxn];
int qu[maxn];
bool pd(double x){
for(int i=1;i<=n;i++){
tmp[i]=(double)a[i]-x;
sum[i]=sum[i-1]+tmp[i];
}
int head=1,tail=0;
int now=0;
double ans=-1e9;
for(int i=1;i<=n;i++){
while(i-now>=l){
while(head<=tail&&sum[qu[tail]]>=sum[now]){
tail--;
}
qu[++tail]=now;
now++;
}
while(head<=tail&&i-qu[head]>r){
head++;
}
if(head<=tail){
ans=max(ans,sum[i]-sum[qu[head]]);
}
}
return ans>=0;
}
int main(){
scanf("%d%d%d",&n,&l,&r);
double fl=0,fr;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
fr=max(fr,(double)a[i]);
}
while(fr-fl>=1e-5){
double mid=(fl+fr)/2;
if(pd(mid)){
fl=mid;
}else{
fr=mid;
}
}
printf("%.4lf",(fl+fr)/2);
return 0;
}
週期串查詢
題目描述
有一個串只包含數字字元。串的長度為 \(n\) ,下標從 \(1\) 開始。
有兩種操作方式:
\(1,l,r,c\) (\(1\leq l\leq r\leq n\) , \(c\) 是數字字元),表示將 \(l\) 到 \(r\) 這一段的字元全部改成c字元;
\(2,l,r,d\) (\(1\leq l\leq r\leq n\) , \(1\leq d\leq r-l+1\)),表示詢問 \(l\) 到 \(r\) 這一段區間內的子串是否是以d為週期的串。
字串 \(s\) 是以 \(x\) \((1\leq x\leq \left |s \right |)\) ,為週期的串的條件是:對於所有的 \(i\) 從 \(1\) 到\(\left |s \right |-x\) , \(s_i = s_i + x\) 都成立。
輸入格式
單組測試資料。
第一行有兩個整數\(n, m ,k (1\leq n\leq 10^5, 1\leq m+k\leq 10^5)\),表示字串長度和修改次數和詢問次數。
第二行給出原始的串包含 \(n\) 位數字字元。
接下來 \(m+k\) 行,每行一個操作。
有兩種形式:
\(1,l,r,с\) (\(1\leq l\leq r\leq n\) , \(c\) 是數字字元);
\(2,l,r,d\) (\(1\leq l\leq r\leq n\) , \(1\leq d\leq r-l+1\))。
輸出格式
對於每一個詢問,如果該段子串是以 \(d\) 為週期的,輸出 \(YES\) ,否則輸出 \(NO\) 。
樣例
樣例輸入
3 1 2
112
2 2 3 1
1 1 3 8
2 1 2 1
樣例輸出
NO
YES
資料範圍與提示
在這個樣例中第一個詢問的時候子串是“ \(12\) ”,他不是以 \(1\) 為週期的,所以答案是 \(NO\) 。第二個詢問的時候子串是“ \(88\) ”,是以 \(1\) 為週期的,所以答案是 \(YES\) 。
思路
\(hash\) 線段樹的老闆子了,但是 \(memset+memcmp\) 也能卡過,而且還不好卡,正解線段樹反而更慢。
程式碼
memset+memcmp
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+50,INF=0x3f3f3f3f;
inline int read(){
int x=0,w=1;
char ch;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
int n,m,k;
char a[maxn];
int main(){
n=read(),m=read(),k=read();
scanf("%s",a+1);
for(int i=1;i<=m+k;i++){
int opt=read(),l=read(),r=read(),x=read();
if(opt==1){
memset(a+l,x+'0',r-l+1);
}else{
if(memcmp(a+l,a+l+x,r-l-x+1)){
puts("NO");
}else{
puts("YES");
}
}
}
return 0;
}
hash
#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
const int maxn=2e7+50,mod=1e9+9,base=17;
inline int read(){
int x=0,w=1;
char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
int n,m;
char s[maxn];
int a[maxn],b[maxn];
struct Node{
int l,r,lazy;
int hash;
}node[maxn];
void Pushup(int x){
node[x].hash=((node[x<<1].hash*a[node[x<<1|1].r-node[x<<1|1].l+1]+node[x<<1|1].hash)%mod+mod)%mod;
}
void Pushdown(int x){
if(node[x].lazy+1){
node[x<<1].lazy=node[x<<1|1].lazy=node[x].lazy;
node[x<<1].hash=b[node[x<<1].r-node[x<<1].l]*node[x].lazy%mod;
node[x<<1|1].hash=b[node[x<<1|1].r-node[x<<1|1].l]*node[x].lazy%mod;
node[x].lazy=-1;
}
return;
}
void Build(int rt,int l,int r){
node[rt].l=l;
node[rt].r=r;
node[rt].lazy=-1;
if(l==r){
node[rt].hash=s[l]-'0'+1;
return;
}
int mid=(node[rt].l+node[rt].r)>>1;
Build(rt<<1,l,mid);
Build(rt<<1|1,mid+1,r);
Pushup(rt);
}
void Add(int rt,int l,int r,int w){
if(node[rt].l==l&&node[rt].r==r){
node[rt].lazy=w;
node[rt].hash=b[node[rt].r-node[rt].l]*w%mod;
return;
}
Pushdown(rt);
int mid=(node[rt].l+node[rt].r)>>1;
if(mid>=r){
Add(rt<<1,l,r,w);
}else if(l>mid){
Add(rt<<1|1,l,r,w);
}else{
Add(rt<<1,l,mid,w);
Add(rt<<1|1,mid+1,r,w);
}
Pushup(rt);
}
int Query(int rt,int l,int r){
if(l>r)return 0;
if(node[rt].l==l&&node[rt].r==r){
return node[rt].hash;
}
Pushdown(rt);
int mid=(node[rt].l+node[rt].r)>>1;
if(mid>=r){
return Query(rt<<1,l,r);
}else if(mid<l){
return Query(rt<<1|1,l,r);
}else {
return (Query(rt<<1,l,mid)*a[r-mid]+Query(rt<<1|1,mid+1,r))%mod;
}
}
signed main(){
n=read();m=read();
scanf("%s",s+1);
a[0]=1;
b[0]=1;
for(int i=1;i<=n;i++){
a[i]=a[i-1]*base%mod;
b[i]=(b[i-1]*base+1)%mod;
}
Build(1,1,n);
while(m--){
int pd=read(),l=read(),r=read(),x=read();
if(pd==1){
Add(1,l,r,x+1);
}else{
if(Query(1,l,r-x)==Query(1,l+x,r)){
printf("YES\n");
}else{
printf("NO\n");
}
}
}
return 0;
}
追捕
題目描述
\(Duan2baka\) :“\(jmsyzsfq\) 天下第一蠢!”
\(jmsyzsfq\) :“你說什麼?!”
於是 \(Duan2baka\) 開始了逃亡的旅程,而 \(jmsyzsfq\) 也開始追捕 \(Duan2baka\) 。
他們來到了一個有 \(n\) 個節點的有向圖,迅速逃跑的 \(Duan2baka\) 會首先降落在有向圖的某個節點T上,因為怕發出聲音,他會永遠靜止不動。而隨後跟來的 \(jmsyzsfq\) 會降落在圖上隨機一個節點 \(S\) 上(可以與 \(T\) 相同),並可以沿著圖中的有向邊隨意走動。
因為 \(jmsyzsfq\) 有著敏銳的嗅覺而且絕頂聰明,他一定會沿著正確的方向尋找 \(Duan2baka\) 。你可以認為,如果圖中存在一條合法的從 \(S\) 到 \(T\) 的路徑,那麼 \(jmsyzsfq\) 一定會抓到 \(Duan2baka\) ——因此 \(jmsyzsfq\) 能否追捕到 \(Duan2baka\) 在他剛剛降落在圖上的時候就已經確定了。
現在請你幫幫 \(jmsyzsfq\) ,在他降落前告訴他有多大的概率能夠追捕到 \(Duan2baka\) ,並告訴他想要追到 \(Duan2baka\) 他可以降落在哪些節點上。
輸入格式
輸入第一行包含三個整數\(n\) , \(m\) , \(opt\) ,表示圖中有 \(n\) 個節點,\(m\) 條有向邊;\(opt\) 為 \(0\) 或 \(1\) ,含義見輸出格式所述。
輸入第 \(2\) 至 \(m+1\) 行每行兩個整數 \(u\) ,\(v\) 描述了圖中一條從 \(u\) 到 \(v\) 的有向邊。
輸入第 \(m+2\) 行一個整數 \(T\) 表示 \(Duan2baka\) 降落的位置。
輸出格式
如果輸入的 \(opt\) 為 \(0\) ,只需要輸出 \(jmsyzsfq\) 能夠追捕到 \(Duan2baka\) 的概率。
如果輸入的 \(opt\) 為 \(1\) ,輸出兩行,第一行輸出 \(jmsyzsfq\) 能夠追捕到 \(Duan2baka\) 的概率;第二行按從小到大輸出想要追到 \(Duan2baka\) 他可以降落的節點編號,編號間用空格隔開。
對於 \(jmsyzsfq\) 能夠追捕到 \(Duan2baka\) 的概率,是一個既約分數,形如“ \(a/b\) ”(\(a,b\) 為整數),使 \(gcd(a,b)=1\) ,如果 \(a=b\) ,輸出 \(1/1\) 。
樣例
樣例輸入 #1
9 10 1
1 2
2 1
2 4
6 1
9 6
6 5
5 3
3 7
3 1
1 8
1
樣例輸出 #1
2/3
1 2 3 5 6 9
樣例 #1解釋
圖中共 \(9\) 個節點,能夠到達節點 \(S=1\) 的節點共 \(6\) 個:\({1,2,3,5,6,9}\) 。所以能夠追捕到 \(Duan2baka\) 的概率為 \(6/9=2/3\) 。
樣例輸入 #2
5 7 1
1 2
1 3
1 5
2 4
4 1
4 5
5 3
1
樣例輸出 #2
3/5
1 2 4
資料範圍與提示
\(opt=0\) 和 \(opt=1\) 的資料各 \(50\%\) 。
對於 \(opt=0\) 和 \(opt=1\) 的情況,有著完全相同的子任務:
對於 \(10\%\) 的資料,\(n,m\leq 100\) 。
對於另外 \(30\%\) 的資料,\(n,m\leq 100,000\) 。
對於另外 \(10%\) 的資料,保證圖是一個完全圖,即對於任意兩個點 \(u\) 和 \(v\) ,必然有兩條有向邊 \(u→v\) 和 \(v→u\) 。
對於另外 \(20\%\) 的資料,保證圖是一個有向無環圖,即對於任意一個點 \(x\) ,不存在圖上一條路徑從 \(x\) 經過其它點後回到 \(x\) 。
對於另外 \(20%\) 的資料,保證如果圖上存在一條邊 \(u→v\) ,一定存在一條邊 \(v→u\) 。
對於 \(100\%\) 的資料,\(n,m\leq 1,000,000\) ,保證資料合法且不存在重邊、自環。
思路
裸的 \(DFS\) ,根本沒有思路可言,反向建邊,反向跑一遍 \(DFS\) ,這不是有手就行???
程式碼
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+50,INF=0x3f3f3f3f;
inline int read(){
int x=0,w=1;
char ch;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
int n,m,opt,s;
int ans;
int vis[maxn];
int path[maxn];
struct Edge{
int to,next;
}e[maxn];
int GCD(int a,int b){
return b==0 ? a : GCD(b,a%b);
}
int tot,head[maxn];
void Add(int u,int v){
e[++tot].to=v;
e[tot].next=head[u];
head[u]=tot;
}
void DFS(int u){
vis[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(vis[v])continue;
vis[v]=1;
DFS(v);
}
}
int main(){
n=read(),m=read(),opt=read();
for(register int i=1;i<=m;i++){
int u=read(),v=read();
Add(v,u);//反向建邊
}
s=read();
DFS(s);//從終點往其他方向跑
for(int i=1;i<=n;i++){
if(vis[i]){
path[++ans]=i;
}
}
int chu=GCD(ans,n);
if(chu!=1){
printf("%d/%d",ans/chu,n/chu);
}else{
printf("%d/%d",ans,n);
}
if(opt==1){
printf("\n");
for(register int i=1;i<=ans;i++){
printf("%d ",path[i]);
}
printf("\n");
}
return 0;
}
不等式
題目描述
小 \(z\) 熱衷於數學。
今天數學課的內容是解不等式: \(L\leq S\times x\leq R\) 。
小z心想這也太簡單了,不禁陷入了深深的思考:假如已知 \(L,R,S,M\) ,滿足 \(L\leq (S\times x)mod M\leq R\) 的最小正整數該怎麼求呢?
輸入格式
第一行包含一個整數 \(T\) ,表示資料組數,接下來是 \(T\) 行,每行為四個正整數 \(M,S,L,R\) 。
輸出格式
對於每組資料,輸出滿足要求的 \(x\) 值,若不存在,輸出 \(-1\) 。
樣例
樣例輸入
1
5 4 2 3
樣例輸出
2
資料範圍與提示
\(30\%\) 的資料中保證有解並且答案小於等於 \(10^6\);
另外 \(20\%\) 的資料中保證 \(L=R\);
\(100\%\) 的資料中 \(T\leq 100,M,S,L,R\leq 10^9\)。
思路
待更新~~~~~~~~~