l q y z NOIP資格選拔賽【總結】
阿新 • • 發佈:2018-12-03
三道題:
T1:玩具迷題
T2:組合數問題
T3:聯合權值
T1:
模擬大水題,只需要按照題目中說的做就好了
只給程式碼,,,
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> #include<string> using namespace std; #define go(i,j,n,k) for(int i=j;i<=n;i+=j) #define fo(i,j,n,k) for(int i=j;i>=n;i-=k) #define rep(i,x) for(int i=h[x];i;i=e[i].nxt) #define mn 200100 #define inf 2147483647 #define ll long long inline int read(){ int x=0,f=1;char ch=getchar(); while(ch>'9' || ch<'0'){if(ch=='-')f=-f;ch=getchar();} while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } struct node{ int poi; string na; } lxx[mn]; int now, n, m; int main(){ freopen("toy.in","r",stdin); freopen("toy.out","w",stdout); n = read(),m = read(); go(i,1,n,1){ lxx[i].poi=read(); cin>>lxx[i].na; } int now=1; go(i,1,m,1){ int s=read(),x=read(); // s == 0 zuo s == 1 you // lxx[now].poi == 0 ? nei : wai // shun jian ni jia // nei : zuo shun you ni // wai : you shun zuo ni // bu gou qu ling // duo yu qu mo if(lxx[now].poi == 0){ if(s==0){ now -= (x % n); if(now <= 0) now += n; }else{ now += (x % n); if(now > n) now %= n; } }else{ if(s==0){ now += (x % n); if(now > n) now %= n; }else{ now -= (x % n); if(now <= 0) now += n; } } } cout << lxx[now].na << "\n"; return 0; }
T2:
我們不難想到在維護楊輝三角的時候直接取模k,
如果每一個詢問要重新遍歷一遍楊輝三角,時間複雜度O(tnm),明顯過不去。
這不就是求區間和嗎?
我們明顯可以拿一個二維字首和去維護。
記住,維護的時候一定不要把非楊輝三角的部分計算上。
程式碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> #include<string> using namespace std; #define go(i,j,n,k) for(int i=j;i<=n;i+=k) #define fo(i,j,n,k) for(int i=j;i>=n;i-=k) #define rep(i,x) for(int i=h[x];i;i=e[i].nxt) #define mn 2018 #define inf 2147483647 #define ll long long #define mod inline int read(){ int x=0,f=1;char ch=getchar(); while(ch>'9' || ch<'0'){if(ch=='-')f=-f;ch=getchar();} while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } ll C[mn][mn]; ll sum[mn][mn]; int T, n, m, k; // er wei qian zhui he int cnt; inline void get_C(int x = 2000){ //puts("lala"); //memset(C,-1,sizeof(C)); go(i,0,x,1) C[i][0] = 1,C[i][i] = 1; go(i,1,x,1) go(j,1,i,1) C[i][j] = ( C[i - 1][j - 1] + C[i - 1][j] ) % k; sum[0][0] = sum[1][0] = sum[0][1] = 0; go(i,1,x,1){ go(j,1,i,1){ sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + ( (C[i][j] == 0 ) ? 1 : 0); } go(j,i+1,x,1){ sum[i][j] = sum[i][j - 1]; } } } inline void Debug(int x = 2000){ //puts("lala"); go(i,0,10,1){ go(j,0,i,1) printf("%4d ", C[i][j]); puts(""); } puts(""); go(i,0,10,1){ go(j,0,i,1) printf("%4d ", sum[i][j]); puts(""); } } int main(){ freopen("combination.in","r",stdin); freopen("combination.out","w",stdout); memset(sum,0,sizeof(sum)); T = read(),k = read(); get_C(); //Debug(); while(T--){ n = read(), m = read(); int ans = sum[n][m]; cout << ans << "\n"; } return 0; }
T3:
我先貼出來我比賽時寫的程式碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> #include<string> using namespace std; #define go(i,j,n,k) for(int i=j;i<=n;i+=j) #define fo(i,j,n,k) for(int i=j;i>=n;i-=k) #define rep(i,x) for(int i=h[x];i;i=e[i].nxt) #define mn 110 #define inf 2147483647 #define ll long long #define mod 10007 inline int read(){ int x=0,f=1;char ch=getchar(); while(ch>'9' || ch<'0'){if(ch=='-')f=-f;ch=getchar();} while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } struct edge{ int v,nxt; }e[mn << 1]; int p,h[mn]; inline void add(int a,int b){ e[++p].nxt=h[a],h[a]=p,e[p].v=b; } ll w[mn]; int n; ll maxx=-1,sum=0; int main(){ freopen("union.in","r",stdin); freopen("union.out","w",stdout); n = read(); go(i,1,n-1,1){ int a = read(), b = read(); add(a,b),add(b,a); } go(i,1,n,1) w[i]=read(); go(u,1,n,1){ rep(i,u){ int v=e[i].v; rep(j,v){ int vv=e[j].v; if(vv == u) continue; maxx = max(maxx,(w[u] * w[vv])); sum = (sum + (w[u] * w[vv]) % mod) % mod; } } } cout << maxx << " " << sum << "\n"; return 0; }
明顯是個暴力。但是這個程式碼如果把mn改為2010,這個題就可以到70分。
為什麼?
程式碼中顯然是三層迴圈,但是,我們如果兩兩配對,n個點就只能配對成\(n^{2}\)個點對,所以可以成聯合權值的點對就更少了,所以裡面巢狀的兩個遍歷鄰接表的迴圈就只可能比\(n^{2}\)更小了。再加上這個題的資料比較鬆,所以,,,
100做法:
我們可以對這棵樹做一個dfs,在dfs的同時維護最大值和總和。然後向上推,直到樹頂,最頂上的就是我們要求的答案。
說白了,就是 樹形DP
程式碼:
如果最後出結果不取模就會只剩50
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<ctime>
using namespace std;
#define go(i,j,n,k) for(int i=j;i<=n;i+=k)
#define fo(i,j,n,k) for(int i=j;i>=n;i-=k)
#define rep(i,x) for(int i=h[x];i;i=e[i].nxt)
#define mn 200010
#define inf 2147483637
#define ll long long
//#define LOCAL
#define Debug(...) fprintf(stderr, __VA_ARGS__)
#define mod 10007
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,w[mn];
struct edge{
int v,nxt;
edge(int _v = 0, int _nxt = 0):v(_v),nxt(_nxt) {}
}e[mn<<1];
int p,h[mn];
inline void add(int a,int b){
e[++p].nxt=h[a],h[a]=p,e[p].v=b;
}
ll sum[mn], fa[mn], maxx[mn];
inline void dfs(int now,int f,int deep){
ll fmax = -1, smax = -1, res = 0;
fa[now] = f;
if(deep >= 3){
sum[now] += w[fa[fa[now]]] * w[now];
sum[now] %= mod;
maxx[now] = sum[now];
}
if(!h[now])
return ;
rep(i,now){
int v = e[i].v;
if(v == f)
continue;
dfs(v, now, deep + 1);
res += w[v];
sum[now] += sum[v];
sum[now] %= mod;
maxx[now] = max(maxx[now], maxx[v]);
if(w[v] >= fmax){
smax = fmax;
fmax = w[v];
}else if(w[v] >= smax){
smax = w[v];
}
}
maxx[now] = max(maxx[now], fmax * smax);
rep(i,now){
int v = e[i].v;
if(v == f)
continue;
res -= w[v];
sum[now] += (res * w[v]) % mod;
sum[now] %= mod;
}
}
int main(){
n=read();
go(i,1,n-1,1){
int a=read(),b=read();
add(a,b),add(b,a);
}
go(i,1,n,1)
w[i]=read();
dfs(1, 0, 1);
cout << maxx[1] << " " << (sum[1] << 1) % mod;
#ifdef LOCAL
Debug("\nMy Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
return 0;
}
這個做法實際上是把求最大值和求和分開寫的。
求最大值不難,我們只需要維護最大值和次大值就好了,記得更新子節點。
如果一棵樹的部分是這樣的:
我們如何求這一部分的和?我們可以把這個寫成
( 1*2 + 1*3 + 1*4 + 2*3 + 2*4 + 3*4 ) * 2
我們可以通過結合律寫成:
( 1 * (2+3+4) + 2 * (3+4) + 3 * 4 ) * 2
這樣,我們就可以把這個父節點的子節點和在遍歷時求出來。我們在求和時,遍歷子節點,遍歷到每個子節點時,把之前求出的和減去當前的點權值,然後乘以當前點權值,是不是就是有關這個子節點的全部的聯合權值和?記得我們在dfs中要把這個子節點的爺爺節點也要算在求的子節點和中。
記得取模!!!
所以,我的成績:
T1 : 100
T2 : 100
T3 : 30 (70)
比賽較水