【8.12校內測試】【容斥原理】【數位DP(二進位制)】【壓維(?)】
emm,今天測試沒有什麼感想。。
1 a
1.1 問題描述
有n 個青蛙,m 個石頭圍成一圈編號為0 m��1,第i 只青蛙每次跳ai 步,這意味著青
蛙能從石頭j mod m 跳到石頭(j + ai) mod m。青蛙每跳一個石頭,就佔領它。每隻青蛙最開
始在0 號石頭,它們可以一直跳下去。這些青蛙最後佔領的石頭編號和為多少?
1.2 輸入
第一行一個整數T,接下來T 組資料,每組資料輸入兩行。
第一行輸入兩個整數n;m。第二行輸入n 個整數ai。
1.3 輸出
對於每組資料輸出一行,形如”Case[空格][井號]X:[空格]Y”。X 為資料編號,從1 開始,Y
為佔領的石頭編號和。(排版有問題)
1.4 輸入輸出樣例1
1.4.1 輸入樣例
3
2 12
9 10
3 60
22 33 66
9 96
81 40 48 32 64 16 96 42 72
1.4.2 輸出樣例
Case ♯1: 42
Case ♯2: 1170
Case ♯3: 1872
1.5 約定和資料範圍
對於30% 的資料, n 10, m 1e3,
對於100% 的資料, 1 T 50, 1 n 1e4, 1 m 1e9, 1 ai 1e9。
把所有的跳躍長度處理出來(就是gcd(m,a[i]),去算貢獻的時候容斥,記錄當前出現的次數和應該出現的次數
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
ll m, a[10005];
int n, flag[10005], ti;
int G[10005], tot, sum, appear[10005], cnt[10005], qwq[10005];
int gcd ( int a, int b ) {
return b == 0 ? a : gcd ( b, a % b );
}
void init ( int x ) {
memset ( qwq, 0, sizeof ( qwq ) );
memset ( appear, 0, sizeof ( appear ) );
for ( int i = 1; i * i <= x; i ++ ) {
if ( x % i == 0 ) {
qwq[++sum] = i;
if ( i * i != x ) qwq[++sum] = x/i;
}
}
if ( x != 1 ) qwq[++sum] = x;////m分解因數,表示可能做出貢獻的跳躍長度,m不算
}
int main ( ) {
freopen ( "a.in", "r", stdin );
freopen ( "a.out", "w", stdout );
int T;
scanf ( "%d", &T );
while ( T -- ) {
memset ( cnt, 0, sizeof ( cnt ) );
memset ( flag, 0, sizeof ( flag ) );
memset ( G, 0, sizeof ( G ) );
sum = 0;
scanf ( "%d%I64d", &n, &m );
for ( int i = 1; i <= n; i ++ )
scanf ( "%I64d", &a[i] );
init ( m );
sort ( qwq + 1, qwq + 1 + sum );
sum --;
for ( int i = 1; i <= n; i ++ ) {
int d = gcd ( m, a[i] );
for ( int j = 1; j <= sum; j ++ )
if ( qwq[j] % d == 0 ) appear[j] = 1;//應該出現的次數
}
ll ans = 0;
for ( int i = 1; i <= sum; i ++ ) {
if ( cnt[i] != appear[i] ) {//cnt是當前已經出現的次數
ll tmp = ( m - 1 ) / qwq[i];
ans += tmp * ( tmp + 1 ) / 2 * qwq[i] * ( appear[i] - cnt[i] );//多了減少了加
for ( int j = i + 1; j <= sum; j ++ )
if ( qwq[j] % qwq[i] == 0 ) cnt[j] += appear[i] - cnt[i];//更新已經出現的次數
}
}
printf ( "Case #%d: %I64d\n", ++ti, ans );
}
return 0;
}
2 b
2.1 問題描述
對於數字n, [1; n] 內,有多少個數可以被由它轉化成的二進位制裡面的1 的個數整除
比如9 的二進位制形式為1001,但是9%2! = 0,所以它不是
比如8 的二進位制形式為1000,8%1 == 0,所以它是
如對於9 來說[1; 9] 內有5 個數字是合法的
2.2 輸入
輸入一個整數n
2.3 輸出
輸出一個整數結果
2.4 輸入輸出樣例1
2.4.1 輸入樣例
153
2.4.2 輸出樣例
42
2.5 約定和資料範圍
對於20% 的資料, n 50000000,
對於100% 的資料, 1 n 1e19。
將所有都統一成二進位制數,做數位dp即可。 【注意】1e19需要用unsigned long long,此時不能memset成-1,記錄vis陣列表示當前狀態是否在當前模數中被計算過(變化過的記憶化
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll unsigned long long
using namespace std;
ll n;
ll dp[70][70][70][2], fig[20], vis[70][70][70][2];
ll dfs ( int dep, int up, int mod, int num, ll sum, int pre_num ) {
if ( vis[dep][num][mod][up] == pre_num ) return dp[dep][num][mod][up];
if ( !dep && mod == 0 && num == pre_num ) return 1;
if ( !dep ) return 0;
int cnt = up ? fig[dep] : 1;
ll ans = 0;
for ( int i = 0; i <= cnt; i ++ ) {
ans += dfs ( dep - 1, up && i == cnt, ( sum * 2 % pre_num + i ) % pre_num, num + i, sum * 2 + i, pre_num );
}
vis[dep][num][mod][up] = pre_num;
dp[dep][num][mod][up] = ans;
return ans;
}
ll work ( ll n ) {
int ee = 0;
while ( n ) {
fig[++ee] = n % 2;
n /= 2;
}
ll ans = 0;
for ( int i = 1; i <= ee; i ++ ) {//列舉1的個數
ans += dfs ( ee, 1, 0, 0, 0, i );
}
return ans;
}
int main ( ) {
freopen ( "b.in", "r", stdin );
freopen ( "b.out", "w", stdout );
scanf ( "%I64d", &n );
ll ans = work ( n );
printf ( "%I64d", ans );
return 0;
}
3 c
3.1 問題描述
有一個n m 的矩陣,左上角為(1; 1) 右下角為(n;m),每個格子邊長為4,每個格子中
央有權值為aij。對於一個格子的頂點,這個點的權值是
, dis 是這個點到
格子(i; j) 中心的歐幾里得距離。求該權值最小的頂點。如有相同權值的頂點,首先選擇x 坐
標較小的頂點,如果還有相同,再選擇y 座標較小的頂點。
3.2 輸入
第一行為兩個整數n;m,
接下來n 行,每行m 個整數,按圖示從左到右,從上到下,表示aij。
3.3 輸出
輸出兩行,第一行一個整數為最優點的最小權值和為多少,第二行兩個整數分別表示最優
點的x 和y 座標。
3.4 輸入輸出樣例1
3.4.1 輸入樣例
2 3
3 4 5
3 9 1
3
3.4.2 輸出樣例
392
1 1
3.5 輸入輸出樣例2
3.5.1 輸入樣例
3 4
1 0 0 0
0 0 3 0
0 0 5 5
3.5.2 輸出樣例
240
2 3
3.6 約定和資料範圍
對於20% 的資料, n or m = 1,
對於100% 的資料, 1 n;m 1000, 1 aij 100000。
可以發現,可以把貢獻拆成行和列分開計算,同一列上所有點在行上受到的其他各列的總貢獻是一樣的,行同理,最後每個點的總價值把行和列加起來即可。【注意】點和格子的座標
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
int n, m;
ll a[1005][1005], line[1005], row[1005], row_sum[1005];
ll line_sum[1005];
int main ( ) {
freopen ( "c.in", "r", stdin );
freopen ( "c.out", "w", stdout );
scanf ( "%d%d", &n, &m );
for ( int i = 1; i <= n; i ++ )
for ( int j = 1; j <= m; j ++ ) {
scanf ( "%I64d", &a[i][j] );
line_sum[j] += a[i][j];
row_sum[i] += a[i][j];
}
for ( int i = 0; i <= m; i ++ )
for ( int j = 1; j <= m; j ++ ) {
if ( j <= i ) {
line[i] += line_sum[j] * ( ( i - j ) * 4 + 2 ) * ( ( i - j ) * 4 + 2 );
} else {
line[i] += line_sum[j] * ( ( j - i - 1 ) * 4 + 2 ) * ( ( j - i - 1 ) * 4 + 2 );
}
}
for ( int i = 0; i <= n; i ++ )
for ( int j = 1; j <= n; j ++ ) {
if ( j <= i ) {
row[i] += row_sum[j] * ( ( i - j ) * 4 + 2 ) * ( ( i - j ) * 4 + 2 );
} else {
row[i] += row_sum[j] * ( ( j - i - 1 ) * 4 + 2 ) * ( ( j - i - 1 ) * 4 + 2 );
}
}
ll ans = 1e18;
int x, y;
for ( int i = 0; i <= n; i ++ )
for ( int j = 0; j <= m; j ++ ) {
ll tmp = line[j] + row[i];
if ( ans > tmp ) {
x = i; y = j;
ans = tmp;
}
}
printf ( "%I64d\n%d %d", ans, x, y );
return 0;
}