今天是我們組出題,也做了一下。過了4題,感覺題目難度不大。多想想歐拉路那道題說不定也可以做。還順帶過了不是今天比賽的一道線段樹合併。感覺代碼能力的確有所提高(算是恢復)。但是離應有的狀態還是差很遠!
今天的幾何題很簡單但是沒有去寫,因爲幾何的板子太陌生。其實幾何的東西不多,只要熟悉幾個基本的模型,就比較好做。一定不要期望ACM隊裏有人幫你寫幾何題,因此這個假期一定要練好幾何!每天至少1道。先把板子敲熟再應用。做到輕鬆過題。
今天下午和晚上的狀態很差,寫題效率極低,必須反思!堅持到最後一刻!不能放鬆!適當的運動後應該靜下心來寫題!控制住自己!高效的寫題!
今天我出的題(雖然不是原創)感覺還是不錯的。然而沒有人來寫,有點小遺憾!
這兩天的題還有4道沒有調你在幹什麼,明天必須一起全部調完!!!
題解:
A:推結論。然後暴力。挺顯然的,就是思維要放開
B:幾何。圓與矩形求交轉化成把矩形擴展(四角變成1/4圓)和圓心運動的直線求交。
今天沒有寫完(打球去了,忘了自己應該做的事!)明天寫
C:簽到題。WA了兩發因爲沒有看清楚題意。題目上說的是prime number
D:每個位置有pi概率爲1,求區間連續1的長度3次方的期望。單點修改。
把dp貢獻差分,維護1,2,3次,矩陣快速冪維護轉移。利用矩陣的結合律(直接DP無法結合)
經典題
//把f數組維護成貢獻增加(前綴和),矩陣快速冪的時候就不用記ans了,優化一維
//線段樹維護矩陣,動態DP,經典思想。因爲矩陣滿足結合律,而直接dp無法統計左邊對右邊的貢獻
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define maxn 200020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8
typedef long long ll;
inline int read(){
register int num = 0;
register char ch = getchar();
while ( ch > '9' || ch < '0' ) ch = getchar();
while ( ch <= '9' && ch >= '0' ) num = num * 10 + ch - '0' , ch = getchar();
return num;
}
int num[10];
inline void write(int x){
register int cnt = 0;
if ( !x ){ printf("0"); return; } //一定要特判0!!!
while ( x ) num[++cnt] = x % 10 , x /= 10;
while ( cnt ) putchar(num[cnt--] + '0');
}
double a[4][4],b[4][4],tmp[4][4],p[maxn],mtx[maxn][4][4];
int ls[maxn],rs[maxn],tot,root;
int n,m,T;
void clear(){
rep(i,0,tot) memset(mtx[i],0,sizeof(mtx[i])) , ls[i] = rs[i] = 0;
tot = root = 0;
}
inline void power(double a[4][4],double b[4][4],double c[4][4]){
memset(tmp,0,sizeof(tmp));
//rep(i,0,4) rep(j,0,4) tmp[i][j] = 0;
rep(i,0,3) rep(j,0,3) rep(k,0,3) tmp[i][j] += b[i][k] * c[k][j];
memcpy(a,tmp,sizeof(tmp));
}
void update(int x){
power(mtx[x],mtx[ls[x]],mtx[rs[x]]);
}
void build(int &x,int l,int r){
x = ++tot;
if ( l == r ){
mtx[x][1][1] = mtx[x][2][2] = mtx[x][0][3] = mtx[x][1][3] = mtx[x][2][3] = p[l];
mtx[x][0][1] = mtx[x][0][2] = p[l] * 3 , mtx[x][1][2] = p[l] * 2;
mtx[x][0][0] = mtx[x][3][3] = 1;
return;
}
int mid = (l + r) >> 1;
build(ls[x],l,mid) , build(rs[x],mid + 1,r);
update(x);
}
void modify(int x,int l,int r,int id,double cp){
if ( l == r ){
p[l] = cp;
mtx[x][1][1] = mtx[x][2][2] = mtx[x][0][3] = mtx[x][1][3] = mtx[x][2][3] = p[l];
mtx[x][0][1] = mtx[x][0][2] = p[l] * 3 , mtx[x][1][2] = p[l] * 2;
mtx[x][0][0] = mtx[x][3][3] = 1;
return;
}
int mid = (l + r) >> 1;
if ( id <= mid ) modify(ls[x],l,mid,id,cp);
else modify(rs[x],mid + 1,r,id,cp);
update(x);
}
void query(int x,int l,int r,int L,int R,double a[4][4]){
if ( L <= l && R >= r ) { power(a,a,mtx[x]); return; }
int mid = (l + r) >> 1;
if ( L <= mid ) query(ls[x],l,mid,L,R,a);
if ( R > mid ) query(rs[x],mid + 1,r,L,R,a);
}
int main(){
// freopen("input.txt","r",stdin);
scanf("%d",&T);
while ( T-- ){
clear();
scanf("%d",&n);
rep(i,1,n) scanf("%lf",&p[i]);
build(root,1,n);
scanf("%d",&m);
while ( m-- ){
int tp,x,l,r; double cp;
scanf("%d",&tp);
if ( tp == 1 ){
scanf("%d %lf",&x,&cp);
modify(root,1,n,x,cp);
}
else{
scanf("%d %d",&l,&r);
memset(a,0,sizeof(a));
rep(i,0,3) a[i][i] = 1;
query(root,1,n,l,r,a);
double ans = a[0][3];
printf("%.2f\n",ans);
}
}
}
return 0;
}
E:一道歐拉回路的好題。[1,m]的數各出現n次,每次與0交換(開始0在末尾)使[k * m + 1,k *m + m]的區間中[1,m]恰好各出現一次。
對於每一個區間,如果一個值多了則向該數連邊,少了則該數向它連邊。對於每個聯通塊找歐拉回路。因爲出入度相等所以一定存在。
輸出方案在歐拉回路退棧的時候推下細節。非常好寫。但是因爲看錯數據範圍調了很久!很不應該!
求歐拉回路要邊要dfs完後加入棧,保證邊的順序
//看清數據範圍再寫題!!!
#include<bits/stdc++.h>
using namespace std;
#define maxn 820
#define N 200020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8
struct node{
int next,to,from;
}e[N];
struct node2{
int x,y;
};
int head[maxn],cnt;
int n,m,T,stack_[N],tops,tag[N];
int a[maxn][maxn],num[maxn][maxn],tmp,vis[maxn];
vector <int> vec[maxn][maxn];
vector <node2> ans;
void clear(){
rep(i,1,n + m) vis[i] = head[i] = 0;
rep(i,1,n) rep(j,1,m) num[i][j] = 0 , vec[i][j].clear();
rep(i,1,cnt) tag[i] = 0;
ans.clear(); cnt = 0;
}
inline void adde(int x,int y){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].from = x;
head[x] = cnt;
}
void dfs(int x){
vis[x] = 1;
for (int i = head[x] ; i ; i = e[i].next){
if ( tag[i] ) continue;
tag[i] = 1;
dfs(e[i].to);
stack_[++tops] = i; //求歐拉回路要後加入棧,保證邊的順序
}
}
int main(){
freopen("input.txt","r",stdin);
freopen("1.out","w",stdout);
while ( ~scanf("%d %d",&n,&m) ){
tmp = n * m + 1;
rep(i,1,n){
rep(j,1,m) scanf("%d",&a[i][j]) , vec[i][a[i][j]].push_back((i - 1) * m + j) , num[i][a[i][j]]++;
rep(j,1,m){
if ( !num[i][j] ) adde(j,m + i);
else rep(k,2,num[i][j]) adde(m + i,j);
}
}
rep(i,m + 1,n + m){
if ( !vis[i] ){
int fir = head[i],id,cur,last,num;
tops = 0 , dfs(i);
if ( !fir ) continue;
last = vec[i - m][e[fir].to].back();
vec[i - m][e[fir].to].pop_back();
ans.push_back((node2){last,tmp});
for(int j = 1 ; j <= tops ;j++){
id = stack_[j], num = e[id].from;
id = stack_[++j];
if ( j == tops ) cur = n * m + 1;
else{
cur = vec[e[id].from - m][num].back();
vec[e[id].from - m][num].pop_back();
}
ans.push_back((node2){last,cur});
last = cur;
}
}
}
printf("%d\n",ans.size());
for (int i = 0 ; i < ans.size() ; i++){
printf("%d %d\n",ans[i].x,ans[i].y);
}
clear();
}
return 0;
}
F:求最少刪多少個數是最長上升子序列變短。
按f[i](dp值)分層建圖。最小割。
好久沒寫網絡流的題。考場上現場yy出來的。
但是應該加強網絡流的練習,別把強項丟了
G:我自己的題。見我的出題日誌。
最後,提醒幾點:
1。早點睡,保證考試時精力充沛!
2。一定要複習,有10多天沒有複習以前的題了
3。堅持到最後一刻,一直到開學都不能有一點鬆懈。把自己的弱項靜心補起來,相信自己OI的實力!
4。明天打好翻盤仗!靜下心來思考!爭取過5題!進入前5名!