今天又只過了一題,連崩三天了!!!說明我的知識還有很多漏洞,各方面能力都還有很大欠缺!這三天的考試都暴露了很大的問題。
1。對算法瞭解不深入,不全面。好多算法只是聽說過,或者略懂一點,但沒有真正深入掌握。比如今天的二分圖博弈,又忘了結論是什麼。
2。寫代碼錯誤太多。這些錯誤在代碼寫得很熟練的時候往往很少犯,所以一定要多練習,慢慢提升自己的代碼能力。如今天F題,dfs前忘了標記vis,導致路徑上有重點,一直沒調出來。
並且,一定要訓練自己用眼調試代碼的能力,邊讀邊想每句代碼的正確性,減少對對拍的依賴,因爲考場上沒時間對拍!
3。思維不夠深入。好多時候模型轉化還不夠,只能想出一個時間複雜度較爲接近的算法,但離正解還有一步,如果去寫通常過不了反而浪費時間。比如今天G題本來是很簡單的結論題,(看錯題)想複雜去分治NTT。還有昨天nlogn求&最大值,只想到sqrt(n)分塊,怎麼優化常數都過不了。這種情況應該更深入的去想正解,不能心存僥倖,去寫時間複雜度明顯錯誤的算法!
4。讀題不夠仔細。英文比賽打得很少,所以對ACM題面的分析能力不夠強。經常看掉題目特殊限制,從而想不出題,想錯題,浪費很多時間。還有ACM中細節比OI中明顯多很多,比如多組數據清空數組,特殊情況判定(特別是ACM必須AC,而OI少特判可能掉10分而已)。所以必須更加細緻,思維更加全面,特別是把做法想出來的題快速寫對!
5。套路不夠熟練。可能是因爲脫離OI太久的緣故但這絕不是藉口!!任何和你一起訓練的人都和你站在同一起跑線上,ACM是新的起點!所以多做題,認真補題,特別是多總結,多思考,多複習,靜心努力,提高自己的水平,讓自己能熟悉的轉化出各種模型!
題解:
A:簽到題。換個順序枚舉即可,思路很常見。應該10分鐘寫完的!
B:數位DP,求迴文數個數(後四位爲日期)。所以枚舉後4爲,對前面的數位DP,記錄是否頂上界,不頂上界直接乘10^len即可
其實很好寫啊!
#include<bits/stdc++.h>
using namespace std;
#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--)
typedef long long ll;
ll power[20];
ll L,R,d1,d2;
ll ans;
int T,mx[20],a[20],rev[10020],tot;
inline void read(ll &x,ll &y){
int num[20],cnt = 0;
register char ch = getchar();
while ( ch > '9' || ch < '0' ) ch = getchar();
while ( ch <= '9' && ch >= '0' ) num[++cnt] = ch - '0' , ch = getchar();
rep(i,cnt - 3,cnt) y = y * 10 + num[i];
rep(i,1,cnt - 4) x = x * 10 + num[i];
}
void init(){
power[0] = 1;
rep(i,1,18) power[i] = power[i - 1] * 10ll;
rep(i,1,12) mx[i] = 31;
mx[2] = 28 , mx[4] = mx[6] = mx[9] = mx[11] = 30;
rep(i,1,2000){
int cnt = 0,x = i;
rep(j,1,4) a[++cnt] = x % 10 , x /= 10;
rep(j,1,4) rev[i] = rev[i] * 10 + a[j];
}
rep(i,1,12) rep(j,1,mx[i]) if ( j % 10 ) tot++;
}
ll DP(int i,int j,int t2){
if ( i < j ){
if ( t2 ) return 0;
return 1;
}
// if ( !t1 ) return power[(i - j + 2) / 2];
ll res = 0;
//頂上界繼續枚舉,注意超過下界主要看當前位
res += DP(i - 1,j + 1,(a[i] > a[j]) || (t2 && (a[i] == a[j])));
//不頂上界直接統計答案,所以不用記錄是否頂上界
res += power[(i - j) / 2] * a[i];
return res;
}
inline ll Calc(ll x,ll y){
int cnt = 0,fir = 0,c = 0; ll res = 0;
memset(a,0,sizeof(a));
while ( x ) a[++cnt] = x % 10 , x /= 10;
repd(i,cnt,cnt - 3) fir = fir * 10 + a[i];
rep(i,1,12){
rep(j,1,mx[i]){
c = i * 100 + j;
if ( (j % 10) == 0 || rev[c] > fir ) continue;
if ( rev[c] == fir ) res += DP(cnt - 4,1,c > y);
else res += power[(cnt - 3) / 2];
}
// cout<<res<<endl;
}
rep(i,0,cnt - 5) res += power[(i + 1) / 2] * tot;
return res;
}
int main(){
freopen("input.txt","r",stdin);
init();
scanf("%d",&T);
while ( T-- ){
L = d1 = R = d2 = 0;
read(L,d1) , read(R,d2);
ans = Calc(R,d2) - Calc(L,d1 - 1);
printf("%lld\n",ans);
}
return 0;
}
C:博弈,每次選一個數,是last+a[x]爲質數,將它刪去。
因爲last+a[x]爲奇數,所以一定一奇一偶,則轉化爲二分圖(奇偶分治)然後就是二分圖博弈。
然而怎麼判斷一個點是否一定在所有最大匹配上。在殘量網絡上bfs,如果S能到x,則x不一定在,否則一定在。注意判2時要刪除0號節點
#include<bits/stdc++.h>
using namespace std;
#define maxn 1020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define inf 1e8
typedef long long ll;
struct node{
int next,to,f;
}e[maxn * maxn * 2];
int head[maxn],cnt = 1,cur[maxn],S,T,dis[maxn],q[maxn],hh,tt;
int a[maxn],n,t,id,cnte,cnto,odd[maxn],even[maxn],maxflow;
void clear(){
rep(i,1,T) head[i] = 0;
cnte = cnto = 0 , cnt = 1 , id = maxflow = 0;
}
inline ll power(ll x,ll y,ll p){
ll res = 1;
while ( y ) {
if ( y & 1 ) res = res * x % p;
x = x * x % p;
y >>= 1;
}
return res;
}
inline bool check(ll a,int x,int d,ll p){
a = power(a,x,p);
if ( a == 1 || a == p - 1 ) return 1;
rep(i,1,d){
a = a * a % p;
if ( a== p - 1) return 1;
}
return 0;
}
inline bool isprime(int p){
if ( p <= 2 ) return 0;
//if ( p == 2 ) return 1;
int x = p - 1,cnt = 0;
while ( !(x & 1) ) x >>= 1 , cnt++;
rep(i,1,10){
int cur = rand() % (p - 1) + 1;
if ( !check(cur,x,cnt,p) ){ return 0; }
}
return 1;
}
inline void adde(int x,int y,int c){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].f = c;
head[x] = cnt;
e[++cnt].to = x;
e[cnt].next = head[y];
e[cnt].f = 0;
head[y] = cnt;
}
bool bfs(){
rep(i,1,T) dis[i] = 0;
tt = hh = 0 , q[tt++] = S , dis[S] = 1;
while ( hh < tt ){
int x = q[hh++];
for (int i = head[x] ; i ; i = e[i].next){
if ( e[i].f && !dis[e[i].to] ){
dis[e[i].to] = dis[x] + 1;
q[tt++] = e[i].to;
}
}
}
return dis[T];
}
int dfs(int x,int delta){
if ( x == T || !delta ) return delta;
int res = 0;
for (int &i = cur[x] ; i ; i = e[i].next){
if ( e[i].f && dis[e[i].to] == dis[x] + 1 ){
int d = dfs(e[i].to,min(delta,e[i].f));
res += d , delta -= d;
e[i].f -= d, e[i ^ 1].f += d;
if ( !delta ) return res;
}
}
if ( delta ) dis[x]= -1;
return res;
}
//直接刪掉x重新跑,看流量是否相等也可以判斷。注意判2的時候0也要刪除
bool check(int x){ //一個點是否在所有最大匹配上
if ( !x ) return 1;
for(int i = 2 ; i <= cnt ; i += 2){
if ( e[i].to == x || e[i ^ 1].to == x || e[i].to == cnte || e[i ^ 1].to == cnte ){
e[i].f = e[i ^ 1].f = 0;
}
else{
e[i].f = 1 , e[i ^ 1].f = 0;
}
}
int c = 0;
while ( bfs() ){
rep(i,1,T) cur[i] = head[i];
c += dfs(S,inf);
}
if ( c == maxflow ) return 0;
return 1;
}
bool bfs2(int x,int y){
tt = hh = 0;
rep(i,1,T) dis[i] = 0;
q[tt++] = S, dis[S] = 1;
while ( hh < tt ){
int x = q[hh++];
for (int i = head[x] ; i ; i = e[i].next){
if ( e[i].f && !dis[e[i].to] && e[i].to != y ){
dis[e[i].to] = dis[x] + 1;
q[tt++] = e[i].to;
}
}
}
return dis[x];
}
//直接在殘量網絡上從S bfs,如果能到x,說明x不一定在最大匹配上,否則一定在
bool check2(){
if ( !bfs2(cnte,0) ) return 0;
for(int i = 2 ; i <= cnt ; i += 2){
if ( e[i].to == cnte || e[i ^ 1].to == cnte ){
e[i].f = e[i ^ 1].f = 0;
}
else{
e[i].f = 1 , e[i ^ 1].f = 0;
}
}
while ( bfs() ){
rep(i,1,T) cur[i] = head[i];
dfs(S,inf);
}
if ( id && bfs2(id,cnte) ) return 0;
return 1;
}
int main(){
freopen("input.txt","r",stdin);
scanf("%d",&t);
while ( t-- ){
clear();
scanf("%d",&n);
int tag = 0;
rep(i,1,n){
scanf("%d",&a[i]);
if ( isprime(a[i]) ) tag = 1;
if ( a[i] & 1 ) odd[++cnto] = a[i];
else even[++cnte] = a[i];
if ( a[i] == 2 ) id = cnte;
}
if ( !tag && !id ){ printf("Totodile\n"); continue; }
even[++cnte] = 0 , S = n + 2, T = n + 3;
rep(i,1,cnto){
rep(j,1,cnte)
if ( isprime(odd[i] + even[j]) ){
adde(j,i + cnte,1);
}
}
rep(i,1,cnto) adde(i + cnte,T,1);
rep(i,1,cnte) adde(S,i,1);
while ( bfs() ){
rep(i,1,T) cur[i] = head[i];
maxflow += dfs(S,inf);
}
//if ( !check(cnte) && check(id) )
if ( check2() ) printf("Totodile\n");
else printf("Bulbasaur\n");
}
return 0;
}
`
D:給n個數填入m個位置,是sigma(|a[i] - b[j]|)最小。
用線段樹維護DP。DP時把匹配看成向左或向右的一段。
好題!但是寫和調花了太長時間!應該快速理清楚思路然後寫!注意細節,其實很好寫,但要推清楚,查錯很難!注意有相同數時只能把a[i]和b[i]的關係定爲一種。
#include<bits/stdc++.h>
using namespace std;
#define maxn 100020
#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 1e18
typedef long long ll;
int a[maxn],b[maxn],c[maxn * 2],n,m,tot;
int T,root[maxn],cnt,ls[maxn << 5],rs[maxn << 5];
ll fl[maxn],mn[maxn << 5],lw[maxn],rw[maxn],suma[maxn],sumb[maxn];
int stack_[maxn],tops,lb[maxn],la[maxn],rb[maxn],ra[maxn],pa[maxn];
struct node{
int id,num,t;
/* bool operator < (node a)const{
if ( id == a.id ) return t > a.t;
return id < a.id;
}*/
}dt[maxn * 2];
inline bool cmp1 (node a,node b){
if ( a.id == b.id ){
if ( a.t == b.t ) return a.num < b.num;
return a.t > b.t;
}
return a.id < b.id;
}
inline bool cmp2(node a,node b){
if ( a.id == b.id ){
if ( a.t == b.t ) return a.num < b.num;
return a.t > b.t;
}
return a.id < b.id;
}
void clear(){
mn[0] = inf;
memset(fl,0x3f,sizeof(fl)) , fl[0] = 0;
rep(i,1,n) root[i] = 0;
rep(i,1,tot) ls[i] = rs[i] = 0;
tot = 0;
rep(i,1,m) lb[i] = la[i] = rb[i] = ra[i] = 0 , pa[i] = n + 1;
}
void pre(){
sort(c + 1,c + tot + 1);
rep(i,1,n) a[i] = lower_bound(c + 1,c + tot + 1,a[i]) - c;
rep(i,1,m) b[i] = lower_bound(c + 1,c + tot + 1,b[i]) - c;
sort(a + 1,a + n + 1) , sort(b + 1,b + m + 1);
int j = 1;
rep(i,1,m){
while ( j <= n && a[j] < b[i] ) j++;
if ( j > n ) break;
pa[i] = j;
}
rep(i,1,m) sumb[i] = sumb[i - 1] + c[b[i]];
rep(i,1,n) suma[i] = suma[i - 1] + c[a[i]];
}
void init(){
int mx = 0,mn = tot + 2;
//b向右匹配
tot = tops = 0;
rep(i,1,n) dt[++tot] = (node){a[i],i,-1};
rep(i,1,m) dt[++tot] = (node){b[i],i,1};
sort(dt + 1,dt + tot + 1,cmp1);
rep(i,1,tot){
int t = dt[i].t,cur = 0;
if ( t == 1 ) stack_[++tops] = dt[i].num , mx = dt[i].num;
else{
if ( !tops ) continue;
cur = stack_[tops--];
ra[cur] = dt[i].num , rb[cur] = mx , rw[cur] = suma[dt[i].num] - suma[pa[cur] - 1] - (sumb[mx] - sumb[cur - 1]);
}
}
//b向左匹配
tot = tops = 0;
rep(i,1,n) dt[++tot] = (node){a[i],i,-1};
rep(i,1,m) dt[++tot] = (node){b[i],i,1};
sort(dt + 1,dt + tot + 1,cmp2);
repd(i,tot,1){
int t = dt[i].t,cur = 0;
if ( t == 1 ) stack_[++tops] = dt[i].num , mn = dt[i].num;
else{
if ( !tops ) continue;
cur = stack_[tops--];
la[cur] = dt[i].num , lb[cur] = mn;
lw[cur] = -(suma[pa[cur] - 1] - suma[dt[i].num - 1]) + sumb[cur] - sumb[mn - 1];
}
}
//rep(i,1,n) cout<<a[i]<<" ";
//rep(i,1,m) cout<<b[i]<<" ";
}
ll query(int x,int l,int r,int L,int R){
if ( !x ) return inf;
if ( L <= l && R >= r ) return mn[x];
ll res = inf; int mid = (l + r) >> 1;
if ( L <= mid ) res = query(ls[x],l,mid,L,R);
if ( R > mid ) res = min(res,query(rs[x],mid + 1,r,L,R));
return res;
}
inline void update(int x){
mn[x] = min(mn[ls[x]],mn[rs[x]]);
}
void modify(int &x,int l,int r,int id,ll d){
if ( !x ) x = ++tot;
if ( l == r ){ mn[x] = d; return; }
int mid = (l + r) >> 1;
if ( id <= mid ) modify(ls[x],l,mid,id,d);
else modify(rs[x],mid + 1,r,id,d);
update(x);
}
void solve(){
tot = 0;
rep(i,1,m){
if ( lb[i] ) fl[i] = min(fl[lb[i] - 1],query(root[lb[i] - 1],1,n,1,la[i] - 1)) + lw[i];
if ( rb[i] ) {
ll cur = min(fl[i - 1],query(root[i - 1],1,n,1,pa[i] - 1)) + rw[i];
modify(root[rb[i]],1,n,ra[i],cur);
}
}
ll ans = min(fl[m],mn[root[m]]);
printf("%lld\n",ans);
}
int main(){
freopen("input.txt","r",stdin);
scanf("%d",&T);
while ( T-- ){
scanf("%d %d",&n,&m);
clear();
rep(i,1,n) scanf("%d",&a[i]) , c[++tot] = a[i];
rep(i,1,m) scanf("%d",&b[i]) , c[++tot] = b[i];
pre();
init();
solve();
}
return 0;
}
E:數學題。
#include<bits/stdc++.h>
using namespace std;
#define maxn 100020
#define rep(i,l,r) for (register int i = l ; i <= r ; i++)
typedef long long ll;
const ll p = 1e9 + 7;
int prime[maxn],cnt,tag[maxn],mul[1020];
int A,B,D,T,a[maxn],tot;
ll ans = 0,inv6;
ll power(ll x,ll y){
ll res = 1;
while ( y ){
if ( y & 1 ) res = res * x % p;
x = x * x % p;
y >>= 1;
}
return res;
}
void init(){
rep(i,2,100000){
if ( !tag[i] ) prime[++cnt] = i;
for(register int j = 1 ; j <= cnt && (i * prime[j]) <= 100000 ; j++){
tag[i * prime[j]] = 1;
if ( (i % prime[j]) == 0 ) break;
}
}
inv6 = power(6,p - 2);
}
inline ll Calc(ll n){
// if ( n & 1 ) n++;
return n * (n + 1) % p * (2 * n + 1) % p * inv6 % p;
}
void solve(){
ans = 0;
rep(i,0,(1 << tot) - 1){
int cur = 1,cnt = 0;
rep(j,0,tot - 1){
if ( i & (1 << j) ) cur *= a[j] , cnt++;
}
if ( cnt & 1 ) ans = (ans - Calc((D + 1)/cur) * cur % p * cur % p + p) % p;
else ans = (ans + Calc((D + 1)/cur) * cur % p * cur) % p;
}
printf("%lld\n",ans * ((ll)A * A % p + (ll)B * B % p) % p);
}
int main(){
freopen("input.txt","r",stdin);
init();
scanf("%d",&T);
while ( T-- ){
scanf("%d %d %d",&A,&B,&D);
if ( D & 1 ){ printf("0\n"); continue; }
else{
tot = 0; int x = D + 2;
//質數要篩到大於sqrt(n)
for (register int i = 1 ; prime[i] * prime[i] <= x ; i++){
if ( (x % prime[i]) == 0 ){
a[tot++] = prime[i];
while ( (x % prime[i]) == 0 ) x /= prime[i];
}
}
if ( x > 1 ) a[tot++] = x;
solve();
}
}
return 0;
}
F:找出一個圖的K染色方案或輸出長度爲K的路徑。
直接構建dfs樹,每層一種顏色(因爲只有返祖邊,同層無邊)
如果無法染色則深度>K
學習了一波圖的K染色方案數(注意不是最大團,不能直接從不能染色的點dfs,明天問一下?)
這裏寫鏈接內容
G:序列只有1和2,問能構成的區間和抑或值。
結論:如果一個序列的一端爲1,則1-sum都可以被表示
挺顯然的,但當時沒有往這方面想,光想着這麼統計和的方案數。開始看成會被抑或掉那種,要求出現次數,所以就想到分治FFT(其實根本不用分治,前綴和卷一下就好,所以FFT技巧也不夠熟練)
然後就找最左或最右的1,v=max(sum[r],sum[n - l + 1])即找出最長可被連續表示的數。然後數爲v +2,v +4,v + 2k
一定要早點睡,我覺得這幾天狀態不好的一大原因就是沒有睡好!任何事都不能與睡覺衝突!安排好時間!