Codeforces Educational Codeforces Round 48
模擬題真實難..嘔
A.Death Note
題意,給你一本書和一個m,每頁上可以寫m個名字,寫滿就要翻頁。再給你n個數字,第i天要寫ai個名字。問你每天各要翻幾頁?
模擬題,沒啥好說的。
B.Segment Occurrences
題意,給你兩個字串s和t。問在s的[l,r]中t出現的次數是多少。又是模擬題,為了方便起見,我們考慮從s中的i位置開始接下來的連續的一串字母是不是和t相同,如果是,說明這個位置開始是可以出現t的。我們對它做一下字首和,就可以輕鬆應付大量的詢問次數。
對於詢問[l,r],我們將r處理為r’=r-t.length()+1。然後如果此時r’已經到l的左邊,那麼無解,否則ans=sum[r’]-sum[l-1]。
#include<iostream>
#include<cstring>
using namespace std;
#define MAX(x,y) (((x)>(y))?(x):(y))
string s,t;
int lens,lent,q;
int num[6005];
int main()
{
cin>>lens>>lent>>q;
cin>>s>>t;
memset(num,0,sizeof(num));
for(int i=0;i<=lens-lent;i++)
{
int flag=0;
for(int j=0;j<lent;j++)if(s[i+j]!=t[j])flag=1;
num[i+1]=num[i];
if(!flag)num[i+1]++;
}
for(int i=0;i<q;i++)
{
int l,r;
cin>>l>>r;
r=r-lent+1;
if(r<l)
{
cout<<0<<endl;
continue ;
}
cout<<num[r]-num[l-1]<<endl;
}
return 0;
}
C.天下第一垃圾模擬題
看到小標題了嗎?好,我們看下一題。
D. Vasya And The Matrix
給你一個n*m的矩陣。然後分別給你每行和每列的xor,問你這個矩陣是什麼。如不存在輸出NO。
我看到第一反應是全部填0,然後右邊和下邊填對應的數,最後確定右下角的格子是否存在這樣的數。但是我沒辦法證明這個演算法是否正確,所以我沒這麼做。聽說是可以過的,希望各位大佬教我證明。
我這邊因為看到xor,所以想到拆位。我們考慮一個base=2^a,窮舉base直到超出範圍,分別統計每一位每個矩陣的格子裡為1還是0,最後加起來得到最終結果。
這樣說有點抽象,我們舉個例子。
比方說題目是這樣(x是要求的數,邊上的數字是異或和)
□ 5 3 13
2 x x x
9 x x x
轉換成二進位制
□□□□ 0101 0011 1101
0010 xxxx xxxx xxxx
1001 xxxx xxxx xxxx
我們先拆base=2^0=1
□ 1 1 1
0 x x x
1 x x x
顯然,對於異或和為1的某行(或某列),其中的1的個數應該是奇數,反之為偶數個,所以橫著的1的個數和豎著的1的個數的奇偶應該相同。否則如果橫著是奇數,豎著是偶數,那麼我們用上面的推論,可以分別得出拆位矩陣中1的個數既是奇數個,又是偶數個,矛盾,vice verse。
當奇偶相同以後,我們用以下的策略填:
1.先找行1和列1的交匯格填1,這樣能解決同樣多個行1和列1
□ 1 1 1
0 x x x
1 1 x x
2.由於上面的推論,一定剩下偶數個行1(或列1),那麼把它們填在同列(同行),因為偶數個所以不會影響所填的列(行)的狀態。
□ 1 1 1
0 x 1 1
1 1 x x
3.其餘位置填0
□ 1 1 1
0 0 1 1
1 1 0 0
最後把表更新進答案就完事了
#include<iostream>
#include<cstring>
using namespace std;
#define LL long long
LL ans[105][105];
int row[105],col[105];
LL a[105],b[105];
LL n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++)cin>>b[i];
memset(ans,0,sizeof(ans));
long long base=1;
while(base<1e9+7)
{
memset(row,0,sizeof(row));
memset(col,0,sizeof(col));
int cntr=0,cntc=0;
for(int i=1;i<=n;i++)if(a[i]&base)row[i]=1,cntr++;
for(int i=1;i<=m;i++)if(b[i]&base)col[i]=1,cntc++;
if((cntr&1)^(cntc&1))break;
if(cntr>cntc)
{
int cnt=cntc;
int pntr=0,pntc=0;
while(cnt--)
{
pntr++;
pntc++;
while(!row[pntr])pntr++;
while(!col[pntc])pntc++;
ans[pntr][pntc]+=base;
}
cnt=cntr-cntc;
while(cnt--)
{
if(cntc==0)pntc=1;
pntr++;
while(!row[pntr])pntr++;
ans[pntr][pntc]+=base;
}
}
else
{
int cnt=cntr;
int pntr=0,pntc=0;
while(cnt--)
{
pntr++;
pntc++;
while(!row[pntr])pntr++;
while(!col[pntc])pntc++;
ans[pntr][pntc]+=base;
}
cnt=cntc-cntr;
while(cnt--)
{
if(cntr==0)pntr=1;
pntc++;
while(!col[pntc])pntc++;
ans[pntr][pntc]+=base;
}
}
base=base*2;
}
if(base<1e9+7)cout<<"NO"<<endl;
else
{
cout<<"YES"<<endl;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)cout<<ans[i][j]<<" ";
cout<<endl;
}
}
}
補充對於填0方案的證明:
首先,我們不妨設兩邊分別為a1,a2…an;b1,b2…bm。
首先,如果,那麼答案一定是NO。
因為a1⊕a2⊕…⊕an和b1⊕b2⊕…⊕bm分別是矩陣中所有元素的異或和。如果他們不相等,那麼顯然是矛盾的。於是我們得到:
a1⊕a2⊕…⊕an⊕b1⊕b2⊕…⊕bm=0 (1)
接下來再看題目,對於題目:
□□ b1 b2 … bm
a1
a2
…
an
我們除了matrix[1][1]之外按照以下的方案填寫(因為排版,所有0用00表示):
□□ b1 b2 … bm
a1 xx b2 … bm
a2 a2 00 … 00
… … … … …
an an 00 … 00
最後只剩下matrix[1][1]沒有解決,我們希望構造出這一個解。由題意,不難得出:
matrix[1][1]⊕a2⊕…⊕an=b1 (2)
matrix[1][1]⊕b2⊕…⊕bm=a2 (3)
移項後得到:
matrix[1][1]=a2⊕…⊕an⊕b1 (4)
matrix[1][1]=b2⊕…⊕bm⊕a2 (5)
好的,那麼接下來這邊只需要證明(4)(5)的右邊相等即可。此時我們對
a1⊕a2⊕…⊕an⊕b1⊕b2⊕…⊕bm=0(1)進行變形,得到:
a2⊕…⊕an⊕b1 = b2⊕…⊕bm⊕a2 = matrix[1][1]
那麼說明這樣的matrix[1][1]是存在且唯一的,於是證明完畢。
E.Rest in the Shades
看似很複雜的一道題,其實還是模擬。。
題意是給你一個勻速移動的光源,再給你一些屏障(在同一條線上),詢問一些點無法被光源照射的時長。這道題因為有兩組大型資料(屏障,點),所以必定是對其中一個進行預處理。那麼我們不難想到,不妨將詢問點作為光源,反向處理,其實本質是一樣的。
為了提高執行效率,我們再處理一下字首和,用二分加速一下(好像不二分的話會TLE),花上大量的時間去模擬。叮!AC!
特別提醒一下。我打二分的時候一直忘記要把上下界定在答案外面,這邊我調了好久才調對,所以略作提醒。此外,用&判斷奇偶性的時候,記住&的優先順序是低於==的,所以判斷為偶數的時候要加括號。
#include<iostream>
#include<cmath>
using namespace std;
const double EPS=1e-6;
double s,a,b,t,l,r;
long long n,q,tmp;
double sum[400005],fence[400005],x,y;
void read(long long &x)
{
x=0;long long f=-1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return;
}
bool equ(double a,double b)
{
if(fabs(a-b)<EPS)return 1;
return 0;
}
int findfi(double x)
{
int ll=0,rr=2*n+1;
while(ll<rr-1)
{
int mid=(ll+rr)>>1;
if(fence[mid]>x)rr=mid;
else ll=mid;
}
return rr;
}
int finden(double x)
{
int ll=0,rr=2*n+1;
while(ll<rr-1)
{
int mid=(ll+rr)>>1;
if(fence[mid]>x)rr=mid;
else ll=mid;
}
return ll;
}
int main()
{
cin>>s>>a>>b;
t=b-a;
read(n);
sum[0]=0;
for(int i=1;i<=2*n;i++)
{
read(tmp);
fence[i]=tmp;
sum[i]=sum[i-1];
if(i%2==0)sum[i]+=fence[i]-fence[i-1];
}
fence[0]=0;fence[2*n+1]=1e10+9;
read(q);
for(int i=0;i<q;i++)
{
read(tmp);x=tmp;
read(tmp);y=tmp;
if(equ(x,a))
{
l=x;
r=(b*y-x*s)/(y-s);
}
else if(equ(x,b))
{
r=x;
l=(a*y-x*s)/(y-s);
}
else
{
l=(a*y-x*s)/(y-s);
r=(b*y-x*s)/(y-s);
}
double zeroo=0;
if(l>fence[2*n]||r<fence[1])printf("%.10f\n",zeroo);
else
{
int fi=findfi(l);
int en=finden(r);
double whole=r-l,partt=sum[en]-sum[fi];
if(en<fi)
{
if(en&1)partt=whole;
else partt=0;
}
else
{
if(fi%2==0)
{
double pre=fence[fi];
partt+=pre-l;
}
if(en&1)
{
double post=fence[en];
partt+=r-post;
}
}
double rat=(partt/whole)*t;
printf("%.10f\n",rat);
}
}
}