1. 程式人生 > 其它 >2021.6.17考試總結[NOIP模擬8]

2021.6.17考試總結[NOIP模擬8]

T1 星際旅行

其實就是求兩條只走一遍的邊的方案數。

考場上第一眼就感覺不可做,後來畫了幾個圖,發現好像只要兩個邊是相連的就可以只走一遍,居然還真拿了30。。

其實是一道尤拉路的題,把每條非自環的邊看作兩條平行的邊,問題就轉變為了刪掉兩條邊,使圖變為尤拉圖。

尤拉圖存在的充要條件是圖聯通,且只有0或2個點的出度為奇數。因為把邊一分為二,所以初始出度都為偶。

所以刪兩條相連的邊是其中一種情況,30pts到手。

另外考慮自環,由於自環不計入出度,所以可以刪掉兩個自環或一個自環和任意一邊。

注意在計算前要先判斷圖是否聯通,且應該是邊聯通,也就是說需要邊能夠一次遍歷完,跟點沒關係。

最後把答案累加即可。

code:

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 int n,m,to[200001],nex[200001],head[100001],num,out[100001],ans,cir;
 5 bool vis[100001];
 6 inline int read(){
 7     int x=0,f=1;
 8     char ch=getchar();
 9     while(ch<'0'||ch>'9')
10     {
11         if(ch=='-') f=-1;
12         ch=getchar();
13 } 14 while(ch<='9'&&ch>='0') 15 { 16 x=(x<<1)+(x<<3)+(ch^48); 17 ch=getchar(); 18 } 19 return x*f; 20 } 21 void write(int x){ 22 if(x<0) putchar('-'), x=-x; 23 if(x>9) write(x/10); 24 putchar(x%10+'0'); 25 } 26 inline void add(int
a,int b){ 27 if(a==b){ cir++; return; } 28 to[++num]=b; nex[num]=head[a]; head[a]=num; out[a]++; 29 to[++num]=a; nex[num]=head[b]; head[b]=num; out[b]++; 30 } 31 void dfs(int s){ 32 vis[s]=1; 33 for(int i=head[s];i;i=nex[i]) 34 if(!vis[to[i]]) dfs(to[i]); 35 } 36 signed main(){ 37 n=read(); m=read(); 38 for(int i=1;i<=m;i++) add(read(),read()); 39 for(int i=1;i<=n;i++) 40 if(out[i]){ dfs(i); break; } 41 for(int i=1;i<=n;i++) 42 if(!vis[i]&&out[i]){ write(0); putchar('\n'); return 0; } 43 if(!num){ write(0); putchar('\n'); return 0; } 44 for(int i=1;i<=n;i++) ans+=out[i]*(out[i]-1)/2; 45 ans+=cir*(cir-1)/2+cir*num/2; 46 write(ans); putchar('\n'); 47 return 0; 48 }
T1

T2 砍樹

考場上看到這題,哎呦我*這不二分嗎。。啥都不說打了個天才二分,結果又是30。。

實際上這道題中d和k並沒有線性關係,d越小k不一定越小,所以二分是假的,拿30算是很高了。

正解是個沒學過的暴力一樣的東西。

要求的d要滿足


化簡一波,令,則

運用整除分塊,天花板函式影象應是幾節平行於x軸的線,每條線左邊界為l,右邊界為r,則有:

只有右端點可能為最優解。每次找出右端點後檢驗是否在取值範圍,更新答案即可。

code:

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 int n,k,a[101],d,num,l,r,ans;
 5 inline int read(){
 6     int x=0,f=1;
 7     char ch=getchar();
 8     while(ch<'0'||ch>'9')
 9     {
10         if(ch=='-') f=-1;
11         ch=getchar();
12     }
13     while(ch<='9'&&ch>='0')
14     {
15         x=(x<<1)+(x<<3)+(ch^48);
16         ch=getchar();
17     }
18     return x*f;
19 }
20 void write(int x){
21     if(x<0) putchar('-'), x=-x;
22     if(x>9) write(x/10);
23     putchar(x%10+'0');
24 }
25 bool check(int d){
26     int res=0;
27     for(int i=1;i<=n;i++) res+=ceil((double)a[i]/d);
28     return res*d<=num;
29 }
30 signed main(){
31     n=read(); k=read();    num+=k;
32     for(int i=1;i<=n;i++) a[i]=read(), num+=a[i];
33     for(l=1;l<=num;l=r+1){
34         r=(num/(num/l));
35         if(check(r)) ans=r<ans?ans:r;
36     }
37     write(ans); putchar('\n');
38     return 0;
39 }
T2

T3 超級樹

比T1更不可做。。賽時最高15分。。

一看題解,果然沒思路的題都是DP

考慮狀態陣列fi,j表示一顆深度為i的超級樹,有j條符合題意的路線的方案數。

每次轉移在當前超級樹上方加一個根節點,考慮fi對fi+1的影響。

列舉當前超級樹左子樹路徑為l,右子樹路徑為r,記num=fi,l*fi,r,則有:

• 什麼也不做 dp[i+1][l+r]+=num
• 根自己作為一條新路徑 dp[i+1][l+r+1]+=num
• 根連線到左子樹(或右子樹)的某條路徑上 dp[i+1][l+r]+=2*num*(l+r)

• 根連線左子樹和右子樹的各一條路徑 dp[i+1][l+r-1]+=2*num*l*r
• 根連線左子樹(或右子樹)的兩條路徑 dp[i+1][l+r-1]+=num*(l*(l-1)+r*(r-1))

初值賦f1,0和f1,1為1,最後答案為fk,1

空間看似會炸,但因為每次第二維最多減一,所以最多隻有第二維為k的狀態可能轉移到最終狀態,所以考慮這前k個即可。

幾個式子合併一下,開個longlong,少模幾次,不然可能會T(至少我們這小破評測機是這樣的

code:

 1 #include<bits/stdc++.h>//T3
 2 #define int long long
 3 using namespace std;
 4 int k,mod,f[301][301];
 5 inline int read(){
 6     int x=0,f=1;
 7     char ch=getchar();
 8     while(ch<'0'||ch>'9')
 9     {
10         if(ch=='-') f=-1;
11         ch=getchar();
12     }
13     while(ch<='9'&&ch>='0')
14     {
15         x=(x<<1)+(x<<3)+(ch^48);
16         ch=getchar();
17     }
18     return x*f;
19 }
20 void write(int x){
21     if(x<0) putchar('-'), x=-x;
22     if(x>9) write(x/10);
23     putchar(x%10+'0');
24 }
25 signed main(){
26     k=read(); mod=read();
27     f[1][1]=f[1][0]=1;
28     for(register signed i=0;i<k;++i)
29         for(register signed j=0;j<k;++j)
30             for(register signed l=0;l<=j;++l){
31                 int r=j-l,num=f[i][l]*f[i][r]%mod;
32                 f[i+1][j]=(f[i+1][j]+num+2*num*j)%mod;
33                 f[i+1][j+1]=(f[i+1][j+1]+num)%mod;
34                 f[i+1][j-1]=(f[i+1][j-1]+2*num*l*r+num*(l*(l-1)+r*(r-1)))%mod;
35             }
36     write(f[k][1]); putchar('\n');
37     return 0;
38 }
T3

T4 求和

超級大水題。。資料水的一匹

就求出lca,然後暴力累乘就完事。當時根本沒想A,一出分人都傻了

洛谷上這題有個子任務hack暴力,可以用字首和優化,預處理出根節點到每個點k次冪的字首和,然後進行O(1)查詢,常數大但複雜度小。

但我還是懶得沒去打。。

code:

 1 #include<bits/stdc++.h>//T4
 2 #define int long long
 3 #define rsigned register signed
 4 using namespace std;
 5 const int p=998244353;
 6 int num,head[300001],nex[600001],to[600001],fa[300001][20],n,t,dep[300001],mem[300001][51];
 7 inline int read(){
 8     int x=0,f=1;
 9     char ch=getchar();
10     while(ch<'0'||ch>'9')
11     {
12         if(ch=='-') f=-1;
13         ch=getchar();
14     }
15     while(ch<='9'&&ch>='0')
16     {
17         x=(x<<1)+(x<<3)+(ch^48);
18         ch=getchar();
19     }
20     return x*f;
21 }
22 void write(int x){
23     if(x<0) putchar('-'), x=-x;
24     if(x>9) write(x/10);
25     putchar(x%10+'0');
26 }
27 inline int qmod(int a,int b){
28     if(mem[a][b])    return mem[a][b];
29     int res=1,A=a,B=b; a%=p;
30     while(b){
31         if(b&1)    res=res*a%p;
32         a=a*a%p;
33         b>>=1;
34     }
35     mem[A][B]=res;
36     return mem[A][B];
37 }
38 inline void add(int a,int b){
39     to[++num]=b; nex[num]=head[a]; head[a]=num;
40     to[++num]=a; nex[num]=head[b]; head[b]=num;
41 }
42 void bfs(){
43     dep[1]=0; t=log2(n)+1;
44     queue<signed>q; q.push(1);
45     while(!q.empty()){
46         signed x=q.front(); q.pop();
47         for(rsigned i=head[x];i;i=nex[i]){
48             signed y=to[i];
49             if(y==1||dep[y])    continue;
50             dep[y]=dep[x]+1;
51             fa[y][0]=x;
52             for(signed j=1;j<=t;j++) fa[y][j]=fa[fa[y][j-1]][j-1];
53             q.push(y);
54         }
55     }
56 }
57 inline int lca(int x,int y){
58     if(x==1||y==1)    return 1;
59     if(dep[x]>dep[y]) swap(x,y);
60     for(rsigned i=t;i>=0;i--)
61         if(dep[fa[y][i]]>=dep[x]) y=fa[y][i];
62     if(x==y)    return x;
63     for(rsigned i=t;i>=0;i--)
64         if(fa[y][i]!=fa[x][i]) y=fa[y][i], x=fa[x][i];
65     return fa[x][0];
66 }
67 signed main(){
68     n=read();
69     for(signed i=1;i<n;i++)    add(read(),read());
70     rsigned m=read();
71     bfs();
72     while(m--){
73         int a=read(),b=read(),c=read(),ans=0;
74         int ca=lca(a,b);
75         while(a!=ca) ans=(ans+qmod(dep[a],c))%p, a=fa[a][0];
76         while(b!=ca) ans=(ans+qmod(dep[b],c))%p, b=fa[b][0];
77         ans=(ans+qmod(dep[ca],c))%p;
78         write(ans); putchar('\n');
79     }
80     return 0;
81 }
T4